diff --git a/category.py b/category.py index 9274074..674c7a2 100644 --- a/category.py +++ b/category.py @@ -3,11 +3,12 @@ # The COPYRIGHT file at the top level of this repository contains the # full copyright notices and license terms. -from trytond.model import ModelView, ModelSQL, fields, Unique, tree +from trytond.model import ModelView, ModelSQL, fields, Unique, tree, sequence_ordered from trytond.transaction import Transaction +from trytond.pool import Pool -class Category(tree(separator='/'), ModelSQL, ModelView): +class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView): 'Category' __name__ = 'cashbook.category' @@ -20,6 +21,7 @@ class Category(tree(separator='/'), ModelSQL, ModelView): company = fields.Many2One(string='Company', model_name='company.company', required=True, ondelete="RESTRICT") + sequence = fields.Integer(string='Sequence', select=True) parent = fields.Many2One(string="Parent", model_name='cashbook.category', ondelete='RESTRICT', left='left', right='right') @@ -50,15 +52,26 @@ class Category(tree(separator='/'), ModelSQL, ModelView): def default_right(): return 0 + def get_long_recname(self, recname): + """ build rec_name with account-no + """ + Configuration = Pool().get('cashbook.configuration') + + l1 = [recname] + + if self.account: + if self.account.code: + cfg1 = Configuration.get_singleton() + if getattr(cfg1, 'cataccno', True) == True: + l1.append('[%s]' % self.account.code) + return ' '.join(l1) + def get_rec_name(self, name): """ short + name """ - l1 = [] - if self.account: - if self.account.code: - l1.append(self.account.code) - l1.append(super(Category, self).get_rec_name(name)) - return ' '.join(l1) + return self.get_long_recname( + super(Category, self).get_rec_name(name) + ) @classmethod def search_rec_name(cls, name, clause): @@ -67,6 +80,7 @@ class Category(tree(separator='/'), ModelSQL, ModelView): return ['OR', super(Category, cls).search_rec_name(name, clause), ('account.rec_name',) + tuple(clause[1:]), + ('account.code',) + tuple(clause[1:]), ] @fields.depends('account') diff --git a/configuration.py b/configuration.py index 40120f7..a7aa753 100644 --- a/configuration.py +++ b/configuration.py @@ -9,6 +9,16 @@ from trytond.pyson import Eval, If from trytond.pool import Pool +field_checked = fields.Boolean(string='Checked', + help='Show cashbook lines in Checked-state.') +field_done = fields.Boolean(string='Done', + help='Show cashbook lines in Done-state.') +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_cataccno = fields.Boolean(string='Category: Show account number', + help='Shows the number of the linked account in the name of a category.') + + class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): 'Configuration' __name__ = 'cashbook.configuration' @@ -25,8 +35,10 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): ('date_from', '<=', Eval('date_to')), ()), ])) - checked = fields.MultiValue(fields.Boolean(string='Checked')) - done = fields.MultiValue(fields.Boolean(string='Done')) + checked = fields.MultiValue(field_checked) + done = fields.MultiValue(field_done) + catnamelong = fields.MultiValue(field_catnamelong) + cataccno = fields.MultiValue(field_cataccno) @classmethod def multivalue_model(cls, field): @@ -34,7 +46,8 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): """ pool = Pool() - if field in ['date_from', 'date_to', 'checked', 'done']: + if field in ['date_from', 'date_to', 'checked', 'done', + 'catnamelong', 'cataccno']: return pool.get('cashbook.configuration_user') return super(Configuration, cls).multivalue_model(field) @@ -46,6 +59,14 @@ class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): def default_done(cls, **pattern): return cls.multivalue_model('done').default_done() + @classmethod + def default_catnamelong(cls, **pattern): + return cls.multivalue_model('catnamelong').default_catnamelong() + + @classmethod + def default_cataccno(cls, **pattern): + return cls.multivalue_model('cataccno').default_cataccno() + # end Configuration @@ -65,13 +86,23 @@ class UserConfiguration(ModelSQL, UserValueMixin): ('date_from', '<=', Eval('date_to')), ()), ]) - checked = fields.Boolean(string='Checked') - done = fields.Boolean(string='Done') + checked = field_checked + done = field_done + catnamelong = field_catnamelong + cataccno = field_cataccno @classmethod def default_checked(cls): return True + @classmethod + def default_catnamelong(cls): + return True + + @classmethod + def default_cataccno(cls): + return True + @classmethod def default_done(cls): return False diff --git a/configuration.xml b/configuration.xml index 53a1817..ad86c6d 100644 --- a/configuration.xml +++ b/configuration.xml @@ -20,5 +20,58 @@ full copyright notices and license terms. --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/line.py b/line.py index 9ee5281..56b8549 100644 --- a/line.py +++ b/line.py @@ -30,91 +30,6 @@ STATES = { DEPENDS=['state', 'state_cashbook'] -class LineContext(ModelView): - 'Line Context' - __name__ = 'cashbook.line.context' - - cashbook = fields.Many2One(string='Cashbook', required=True, - model_name='cashbook.book', - states={ - 'readonly': Eval('num_cashbook', 0) < 2, - }, depends=['num_cashbook']) - date_from = fields.Date(string='Start Date', depends=['date_to'], - domain=[ - If(Eval('date_to') & Eval('date_from'), - ('date_from', '<=', Eval('date_to')), - ()), - ]) - date_to = fields.Date(string='End Date', depends=['date_from'], - domain=[ - If(Eval('date_to') & Eval('date_from'), - ('date_from', '<=', Eval('date_to')), - ()), - ]) - checked = fields.Boolean(string='Checked', - help='Show account lines in Checked-state.') - done = fields.Boolean(string='Done', - help='Show account lines in Done-state.') - num_cashbook = fields.Function(fields.Integer(string='Number of Cashbook', - readonly=True, states={'invisible': True}), - 'on_change_with_num_cashbook') - - @classmethod - def default_cashbook(cls): - """ get default from context - """ - context = Transaction().context - return context.get('cashbook', None) - - @classmethod - def default_date_from(cls): - """ get default from context - """ - context = Transaction().context - return context.get('date_from', None) - - @classmethod - def default_date_to(cls): - """ get default from context - """ - context = Transaction().context - return context.get('date_to', None) - - @classmethod - def default_checked(cls): - """ get default from context - """ - context = Transaction().context - return context.get('checked', False) - - @classmethod - def default_num_cashbook(cls): - """ get default from context - """ - CashBook = Pool().get('cashbook.book') - - with Transaction().set_context({ - '_check_access': True, - }): - return CashBook.search_count([]) - - @classmethod - def default_done(cls): - """ get default from context - """ - context = Transaction().context - return context.get('done', False) - - def on_change_with_num_cashbook(self, name=None): - """ get number of accessible cashbooks, - depends on user-permissions - """ - LineContext = Pool().get('cashbook.line.context') - return LineContext.default_num_cashbook() - -# end LineContext - - class Line(Workflow, ModelSQL, ModelView): 'Cashbook Line' __name__ = 'cashbook.line' @@ -130,6 +45,8 @@ class Line(Workflow, ModelSQL, ModelView): category = fields.Many2One(string='Category', required=True, model_name='cashbook.category', ondelete='RESTRICT', states=STATES, depends=DEPENDS) + category_view = fields.Function(fields.Char(string='Category', readonly=True), + 'on_change_with_category_view', searcher='search_category_view') state = fields.Selection(string='State', required=True, readonly=True, select=True, selection=sel_linetype) state_string = state.translated('state') @@ -221,6 +138,39 @@ class Line(Workflow, ModelSQL, ModelView): 'desc': (self.description or '-')[:40], } + @staticmethod + def order_category_view(tables): + """ order: name + """ + table, _ = tables[None] + Category = Pool().get('cashbook.category') + tab_cat = Category.__table__() + + tab2 = tab_cat.select(tab_cat.name, + where=tab_cat.id==table.category + ) + + 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 + """ + Configuration = Pool().get('cashbook.configuration') + if self.category: + cfg1 = Configuration.get_singleton() + + if getattr(cfg1, 'catnamelong', True) == True: + return self.category.rec_name + else : + return self.category.get_long_recname(self.category.name) + @classmethod def search_rec_name(cls, name, clause): """ search in description +... @@ -306,3 +256,88 @@ class Line(Workflow, ModelSQL, ModelView): return super(Line, cls).delete(lines) # end Line + + +class LineContext(ModelView): + 'Line Context' + __name__ = 'cashbook.line.context' + + cashbook = fields.Many2One(string='Cashbook', required=True, + model_name='cashbook.book', + states={ + 'readonly': Eval('num_cashbook', 0) < 2, + }, depends=['num_cashbook']) + date_from = fields.Date(string='Start Date', depends=['date_to'], + domain=[ + If(Eval('date_to') & Eval('date_from'), + ('date_from', '<=', Eval('date_to')), + ()), + ]) + date_to = fields.Date(string='End Date', depends=['date_from'], + domain=[ + If(Eval('date_to') & Eval('date_from'), + ('date_from', '<=', Eval('date_to')), + ()), + ]) + checked = fields.Boolean(string='Checked', + help='Show account lines in Checked-state.') + done = fields.Boolean(string='Done', + help='Show account lines in Done-state.') + num_cashbook = fields.Function(fields.Integer(string='Number of Cashbook', + readonly=True, states={'invisible': True}), + 'on_change_with_num_cashbook') + + @classmethod + def default_cashbook(cls): + """ get default from context + """ + context = Transaction().context + return context.get('cashbook', None) + + @classmethod + def default_date_from(cls): + """ get default from context + """ + context = Transaction().context + return context.get('date_from', None) + + @classmethod + def default_date_to(cls): + """ get default from context + """ + context = Transaction().context + return context.get('date_to', None) + + @classmethod + def default_checked(cls): + """ get default from context + """ + context = Transaction().context + return context.get('checked', False) + + @classmethod + def default_num_cashbook(cls): + """ get default from context + """ + CashBook = Pool().get('cashbook.book') + + with Transaction().set_context({ + '_check_access': True, + }): + return CashBook.search_count([]) + + @classmethod + def default_done(cls): + """ get default from context + """ + context = Transaction().context + return context.get('done', False) + + def on_change_with_num_cashbook(self, name=None): + """ get number of accessible cashbooks, + depends on user-permissions + """ + LineContext = Pool().get('cashbook.line.context') + return LineContext.default_num_cashbook() + +# end LineContext diff --git a/locale/de.po b/locale/de.po index 5f84aea..5e632e7 100644 --- a/locale/de.po +++ b/locale/de.po @@ -46,6 +46,10 @@ msgctxt "model:ir.message,text:msg_category_name_unique" msgid "The category name already exists at this level." msgstr "Der Kategoriename existiert auf dieser Ebene bereits." +msgctxt "model:ir.message,text:msg_category_account_unique" +msgid "The account is already in use for a category." +msgstr "Das Konto wird bereits für eine Kategorie verwendet." + ############# # res.group # @@ -286,6 +290,10 @@ msgctxt "field:cashbook.line,category:" msgid "Category" msgstr "Kategorie" +msgctxt "field:cashbook.line,category_view:" +msgid "Category" +msgstr "Kategorie" + msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current" msgid "Current Month" msgstr "aktueller Monat" @@ -482,6 +490,10 @@ msgctxt "view:cashbook.configuration:" msgid "Open Cashbook Wizard" msgstr "Dialog: Kassenbuch öffnen" +msgctxt "view:cashbook.configuration:" +msgid "Cashbook" +msgstr "Kassenbuch" + msgctxt "field:cashbook.configuration,date_from:" msgid "Start Date" msgstr "Beginndatum" @@ -494,10 +506,34 @@ msgctxt "field:cashbook.configuration,checked:" msgid "Checked" msgstr "Geprüft" +msgctxt "help:cashbook.configuration,checked:" +msgid "Show cashbook lines in Checked-state." +msgstr "Zeigt Kassenbuchzeilen im 'Geprüft'-Status" + msgctxt "field:cashbook.configuration,done:" msgid "Done" msgstr "Fertig" +msgctxt "help:cashbook.configuration,done:" +msgid "Show cashbook lines in Done-state." +msgstr "Zeigt Kassenbuchzeilen im 'Fertig'-Status" + +msgctxt "field:cashbook.configuration,catnamelong:" +msgid "Category: Show long name" +msgstr "Kategorie: langen Namen zeigen" + +msgctxt "help:cashbook.configuration,catnamelong:" +msgid "Shows the long name of the category in the Category field of a cash book line." +msgstr "Zeigt im Feld 'Kategorie' einer Kassenbuchzeile den langen Namen der Kategorie." + +msgctxt "field:cashbook.configuration,cataccno:" +msgid "Category: Show account number" +msgstr "Kategorie: Kontonummer zeigen" + +msgctxt "help:cashbook.configuration,cataccno:" +msgid "Shows the number of the linked account in the name of a category." +msgstr "Zeigt im Namen einer Kategorie die Nummer des verknüpften Kontos." + ############################### # cashbook.configuration_user # @@ -518,6 +554,30 @@ msgctxt "field:cashbook.configuration_user,checked:" msgid "Checked" msgstr "Geprüft" +msgctxt "help:cashbook.configuration_user,checked:" +msgid "Show cashbook lines in Checked-state." +msgstr "Zeigt Kassenbuchzeilen im 'Geprüft'-Status" + msgctxt "field:cashbook.configuration_user,done:" msgid "Done" msgstr "Fertig" + +msgctxt "help:cashbook.configuration_user,done:" +msgid "Show cashbook lines in Done-state." +msgstr "Zeigt Kassenbuchzeilen im 'Fertig'-Status" + +msgctxt "field:cashbook.configuration_user,catnamelong:" +msgid "Category: Show long name" +msgstr "Kategorie: langen Namen zeigen" + +msgctxt "help:cashbook.configuration_user,catnamelong:" +msgid "Shows the long name of the category in the Category field of a cash book line." +msgstr "Zeigt im Feld 'Kategorie' einer Kassenbuchzeile den langen Namen der Kategorie." + +msgctxt "field:cashbook.configuration_user,cataccno:" +msgid "Category: Show account number" +msgstr "Kategorie: Kontonummer zeigen" + +msgctxt "help:cashbook.configuration_user,cataccno:" +msgid "Shows the number of the linked account in the name of a category." +msgstr "Zeigt im Namen einer Kategorie die Nummer des verknüpften Kontos." diff --git a/menu.xml b/menu.xml index 90524d3..4dbd7e3 100644 --- a/menu.xml +++ b/menu.xml @@ -25,6 +25,10 @@ full copyright notices and license terms. --> + + + + @@ -63,7 +67,7 @@ full copyright notices and license terms. --> diff --git a/message.xml b/message.xml index 7bb74a7..9ced7e9 100644 --- a/message.xml +++ b/message.xml @@ -35,6 +35,9 @@ full copyright notices and license terms. --> The category name already exists at this level. + + The account is already in use for a category. + diff --git a/tests/test_category.py b/tests/test_category.py index 9ee834c..c3b5102 100644 --- a/tests/test_category.py +++ b/tests/test_category.py @@ -142,7 +142,7 @@ class CategoryTestCase(ModuleTestCase): 'account': account.id, }]) self.assertEqual(cat1.name, 'Test 1') - self.assertEqual(cat1.rec_name, '0123 Test 1') + self.assertEqual(cat1.rec_name, 'Test 1 [0123]') self.assertEqual(cat1.description, 'Info') self.assertEqual(cat1.company.rec_name, 'm-ds') diff --git a/tests/test_config.py b/tests/test_config.py index 4e0ec0e..6f95827 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -28,6 +28,8 @@ class ConfigTestCase(ModuleTestCase): self.assertEqual(cfg2.date_to, None) self.assertEqual(cfg2.checked, True) self.assertEqual(cfg2.done, False) + self.assertEqual(cfg2.catnamelong, True) + self.assertEqual(cfg2.cataccno, True) @with_transaction() def test_config_create_multi_user(self): @@ -36,13 +38,18 @@ class ConfigTestCase(ModuleTestCase): pool = Pool() Configuration = pool.get('cashbook.configuration') ResUser = pool.get('res.user') + ResGroup = pool.get('res.group') + + grp_cb, = ResGroup.search([('name', '=', 'Cashbook')]) usr_lst = ResUser.create([{ 'login': 'frida', 'name': 'Frida', + 'groups': [('add', [grp_cb.id])], }, { 'login': 'diego', 'name': 'Diego', + 'groups': [('add', [grp_cb.id])], }]) self.assertEqual(len(usr_lst), 2) self.assertEqual(usr_lst[0].name, 'Frida') diff --git a/tests/test_line.py b/tests/test_line.py index 1c3579f..b6a0fa5 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -159,6 +159,98 @@ class LineTestCase(ModuleTestCase): IrDate.today = MagicMock(return_value=date.today()) + @with_transaction() + def test_line_create_check_category_view(self): + """ create cashbook + line, check 'category_view' + """ + pool = Pool() + Book = pool.get('cashbook.book') + Types = pool.get('cashbook.type') + Line = pool.get('cashbook.line') + Configuration = pool.get('cashbook.configuration') + Category = pool.get('cashbook.category') + Account = pool.get('account.account') + + types, = Types.search([('short', '=','CAS')]) + category = self.prep_category() + company = category.company + + with Transaction().set_context({ + 'company': company.id, + }): + accounts = Account.create([{ + 'name': 'Account No 1', + 'code': '0123', + }, { + 'name': 'Account No 2', + 'code': '2345', + }]) + self.assertEqual(accounts[0].rec_name, '0123 - Account No 1') + self.assertEqual(accounts[1].rec_name, '2345 - Account No 2') + + category2, = Category.create([{ + 'company': company.id, + 'name': 'Level1', + 'account': accounts[0].id, + 'childs': [('create', [{ + 'company': company.id, + 'name': 'Level2', + 'account': accounts[1].id, + }])], + }]) + self.assertEqual(category2.rec_name, 'Level1 [0123]') + self.assertEqual(len(category2.childs), 1) + self.assertEqual(category2.childs[0].rec_name, 'Level1/Level2 [2345]') + + cfg1 = Configuration() + cfg1.save() + + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'lines': [('create', [{ + 'date': date(2022, 5, 1), + 'description': 'Text 1', + 'category': category2.id, + }, { + 'date': date(2022, 6, 1), + 'description': 'Text 2', + 'category': category2.childs[0].id, + }])], + }]) + self.assertEqual(book.name, 'Book 1') + self.assertEqual(book.state, 'open') + self.assertEqual(len(book.lines), 2) + + self.assertEqual(cfg1.catnamelong, True) + self.assertEqual(cfg1.cataccno, True) + + self.assertEqual(book.lines[0].category.rec_name, 'Level1 [0123]') + self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2 [2345]') + self.assertEqual(book.lines[0].category_view, 'Level1 [0123]') + self.assertEqual(book.lines[1].category_view, 'Level1/Level2 [2345]') + + cfg1.cataccno = False + cfg1.save() + self.assertEqual(book.lines[0].category.rec_name, 'Level1') + self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2') + self.assertEqual(book.lines[0].category_view, 'Level1') + self.assertEqual(book.lines[1].category_view, 'Level1/Level2') + + cfg1.catnamelong = False + cfg1.save() + self.assertEqual(book.lines[0].category.rec_name, 'Level1') + self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2') + self.assertEqual(book.lines[0].category_view, 'Level1') + self.assertEqual(book.lines[1].category_view, 'Level2') + + cfg1.cataccno = True + cfg1.save() + self.assertEqual(book.lines[0].category.rec_name, 'Level1 [0123]') + self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2 [2345]') + self.assertEqual(book.lines[0].category_view, 'Level1 [0123]') + self.assertEqual(book.lines[1].category_view, 'Level2 [2345]') + @with_transaction() def test_line_delete_with_book_in_open_state(self): """ create cashbook + line, book in state=open, delete a line diff --git a/view/category_form.xml b/view/category_form.xml index b661707..cf733f1 100644 --- a/view/category_form.xml +++ b/view/category_form.xml @@ -17,6 +17,8 @@ full copyright notices and license terms. -->