From d6a8b254a39470ff881cfc50833af41a88585384 Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Tue, 9 Aug 2022 15:08:41 +0200 Subject: [PATCH] neu: kategorie, config + test --- __init__.py | 5 + book.py | 3 + category.py | 85 +++++++ category.xml | 91 +++++++ configuration.py | 79 ++++++ configuration.xml | 24 ++ line.py | 19 +- locale/de.po | 133 +++++++++- locale/en.po | 368 ++++++++++++++++++++++++++++ menu.xml | 21 +- message.xml | 6 + model.py | 47 ++++ tests/__init__.py | 4 + tests/test_book.py | 11 +- tests/test_category.py | 137 +++++++++++ tests/test_config.py | 96 ++++++++ tests/test_line.py | 7 +- tests/test_type.py | 7 +- tryton.cfg | 3 + types.py | 6 +- view/book_form.xml | 4 + view/book_list.xml | 1 + view/cashbook_line_context_form.xml | 2 + view/category_form.xml | 24 ++ view/category_list.xml | 8 + view/category_tree.xml | 10 + view/configuration_form.xml | 14 ++ wizard_openline.py | 75 ++++-- 28 files changed, 1255 insertions(+), 35 deletions(-) create mode 100644 category.py create mode 100644 category.xml create mode 100644 configuration.py create mode 100644 configuration.xml create mode 100644 locale/en.po create mode 100644 model.py create mode 100644 tests/test_category.py create mode 100644 tests/test_config.py create mode 100644 view/category_form.xml create mode 100644 view/category_list.xml create mode 100644 view/category_tree.xml create mode 100644 view/configuration_form.xml diff --git a/__init__.py b/__init__.py index 7d33883..21d3742 100644 --- a/__init__.py +++ b/__init__.py @@ -8,10 +8,15 @@ from .book import Book from .types import Type from .line import Line, LineContext from .wizard_openline import OpenCashBook, OpenCashBookStart +from .configuration import Configuration, UserConfiguration +from .category import Category def register(): Pool.register( + Configuration, + UserConfiguration, Type, + Category, Book, LineContext, Line, diff --git a/book.py b/book.py index bae7cfd..37e8320 100644 --- a/book.py +++ b/book.py @@ -42,6 +42,9 @@ class Book(Workflow, ModelSQL, ModelView): lines = fields.One2Many(string='Lines', field='cashbook', model_name='cashbook.line', states=STATES, depends=DEPENDS) + account = fields.Many2One(string='Account', select=True, + model_name='account.account', ondelete='RESTRICT', + states=STATES, depends=DEPENDS) state = fields.Selection(string='State', required=True, readonly=True, selection=sel_state_book) state_string = state.translated('state') diff --git a/category.py b/category.py new file mode 100644 index 0000000..9274074 --- /dev/null +++ b/category.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# 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. + +from trytond.model import ModelView, ModelSQL, fields, Unique, tree +from trytond.transaction import Transaction + + +class Category(tree(separator='/'), ModelSQL, ModelView): + 'Category' + __name__ = 'cashbook.category' + + name = fields.Char(string='Name', required=True, translate=True) + description = fields.Char(string='Description', translate=True) + account = fields.Many2One(string='Account', select=True, + model_name='account.account', ondelete='RESTRICT') + account_code = fields.Function(fields.Char(string='Account', readonly=True), + 'on_change_with_account_code', searcher='search_account_code') + + company = fields.Many2One(string='Company', model_name='company.company', + required=True, ondelete="RESTRICT") + parent = fields.Many2One(string="Parent", + model_name='cashbook.category', ondelete='RESTRICT', + left='left', right='right') + childs = fields.One2Many(string='Children', field='parent', + model_name='cashbook.category') + left = fields.Integer(string='Left', required=True, select=True) + right = fields.Integer(string='Right', required=True, select=True) + + @classmethod + def __setup__(cls): + super(Category, cls).__setup__() + cls._order.insert(0, ('name', 'ASC')) + t = cls.__table__() + cls._sql_constraints.extend([ + ('name_uniq', Unique(t, t.name, t.company, t.parent), 'cashbook.msg_category_name_unique'), + ('account_uniq', Unique(t, t.account, t.company), 'cashbook.msg_category_account_unique'), + ]) + + @staticmethod + def default_company(): + return Transaction().context.get('company') or None + + @staticmethod + def default_left(): + return 0 + + @staticmethod + def default_right(): + return 0 + + 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) + + @classmethod + def search_rec_name(cls, name, clause): + """ search in account + name + """ + return ['OR', + super(Category, cls).search_rec_name(name, clause), + ('account.rec_name',) + tuple(clause[1:]), + ] + + @fields.depends('account') + def on_change_with_account_code(self, name=None): + """ get code of account + """ + if self.account: + return self.account.code + + @classmethod + def search_account_code(cls, names, clause): + """ search in code + """ + return [('account.code',) + tuple(clause[1:])] + +# end Category diff --git a/category.xml b/category.xml new file mode 100644 index 0000000..7258106 --- /dev/null +++ b/category.xml @@ -0,0 +1,91 @@ + + + + + + + + cashbook.category + tree + + category_list + + + cashbook.category + tree + + childs + category_tree + + + cashbook.category + form + + category_form + + + + + Category + cashbook.category + + + + + + + + + + + + + + + Category + cashbook.category + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration.py b/configuration.py new file mode 100644 index 0000000..40120f7 --- /dev/null +++ b/configuration.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# 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. + +from trytond.model import ModelSingleton, ModelView, ModelSQL, Workflow, fields, Check +from .model import UserMultiValueMixin, UserValueMixin +from trytond.pyson import Eval, If +from trytond.pool import Pool + + +class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin): + 'Configuration' + __name__ = 'cashbook.configuration' + + date_from = fields.MultiValue(fields.Date(string='Start Date', depends=['date_to'], + domain=[ + If(Eval('date_to') & Eval('date_from'), + ('date_from', '<=', Eval('date_to')), + ()), + ])) + date_to = fields.MultiValue(fields.Date(string='End Date', depends=['date_from'], + domain=[ + If(Eval('date_to') & Eval('date_from'), + ('date_from', '<=', Eval('date_to')), + ()), + ])) + checked = fields.MultiValue(fields.Boolean(string='Checked')) + done = fields.MultiValue(fields.Boolean(string='Done')) + + @classmethod + def multivalue_model(cls, field): + """ get model for value + """ + pool = Pool() + + if field in ['date_from', 'date_to', 'checked', 'done']: + return pool.get('cashbook.configuration_user') + return super(Configuration, cls).multivalue_model(field) + + @classmethod + def default_checked(cls, **pattern): + return cls.multivalue_model('checked').default_checked() + + @classmethod + def default_done(cls, **pattern): + return cls.multivalue_model('done').default_done() + +# end Configuration + + +class UserConfiguration(ModelSQL, UserValueMixin): + 'User Configuration' + __name__ = 'cashbook.configuration_user' + + 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') + done = fields.Boolean(string='Done') + + @classmethod + def default_checked(cls): + return True + + @classmethod + def default_done(cls): + return False + +# end UserConfiguration diff --git a/configuration.xml b/configuration.xml new file mode 100644 index 0000000..53a1817 --- /dev/null +++ b/configuration.xml @@ -0,0 +1,24 @@ + + + + + + + cashbook.configuration + form + configuration_form + + + Configuration + cashbook.configuration + + + + + + + + + diff --git a/line.py b/line.py index 3d5d71d..13ef07f 100644 --- a/line.py +++ b/line.py @@ -12,13 +12,13 @@ from trytond.exceptions import UserError from trytond.i18n import gettext from .book import sel_state_book + sel_linetype = [ ('edit', 'Edit'), ('check', 'Checked'), ('done', 'Done'), ] - STATES = { 'readonly': Or( Eval('state', '') != 'edit', @@ -85,6 +85,17 @@ class LineContext(ModelView): 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 @@ -96,8 +107,8 @@ class LineContext(ModelView): """ get number of accessible cashbooks, depends on user-permissions """ - CashBook = Pool().get('cashbook.book') - return CashBook.search_count([]) + LineContext = Pool().get('cashbook.line.context') + return LineContext.default_num_cashbook() # end LineContext @@ -119,6 +130,8 @@ class Line(Workflow, ModelSQL, ModelView): readonly=True, states={'invisible': True}, selection=sel_state_book), 'on_change_with_state_cashbook', searcher='search_state_cashbook') + #image = fields.Binary... + @classmethod def __setup__(cls): super(Line, cls).__setup__() diff --git a/locale/de.po b/locale/de.po index 5372a08..e0682d7 100644 --- a/locale/de.po +++ b/locale/de.po @@ -38,6 +38,13 @@ msgctxt "model:ir.message,text:msg_line_deny_delete2" msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'." msgstr "Die Kassenbuchzeile '%(linetxt)s' kann nicht gelöscht werden, da sie im Status '%(linestate)s' ist." +msgctxt "model:ir.message,text:msg_setting_already_exists" +msgid "Settings for this user alredy exists." +msgstr "Einstellungen für diesen Benutzer sind bereits vorhanden." + +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." ############# @@ -103,6 +110,14 @@ msgctxt "model:ir.ui.menu,name:menu_open_lines" msgid "Open Cashbook" msgstr "Kassenbuch öffnen" +msgctxt "model:ir.ui.menu,name:menu_category" +msgid "Category" +msgstr "Kategorie" + +msgctxt "model:ir.ui.menu,name:menu_category_list" +msgid "Category" +msgstr "Kategorie" + ############# # ir.action # @@ -119,10 +134,14 @@ msgctxt "model:ir.action,name:act_line_view" msgid "Cashbook Line" msgstr "Kassenbuchzeile" -msgctxt "model:ir.action,name:act_open_chart" +msgctxt "model:ir.action,name:act_open_lines" msgid "Open Cashbook" msgstr "Kassenbuch öffnen" +msgctxt "model:ir.ui.menu,name:act_category_view" +msgid "Category" +msgstr "Kategorie" + ################### # ir.model.button # @@ -207,6 +226,10 @@ msgctxt "help:cashbook.book,observer:" msgid "Group of users who have read-only access to the cashbook." msgstr "Gruppe von Benutzern, die nur Lesezugriff auf das Kassenbuch haben." +msgctxt "field:cashbook.book,account:" +msgid "Account" +msgstr "Konto" + ################# # cashbook.line # @@ -280,6 +303,58 @@ msgid "Fixed-term deposit" msgstr "Festgeld" +##################### +# cashbook.category # +##################### +msgctxt "model:cashbook.category,name:" +msgid "Category" +msgstr "Kategorie" + +msgctxt "view:cashbook.category:" +msgid "General Information" +msgstr "Allgemeine Informationen" + +msgctxt "view:cashbook.category:" +msgid "Children" +msgstr "Untergeordnet" + +msgctxt "field:cashbook.category,name:" +msgid "Name" +msgstr "Name" + +msgctxt "field:cashbook.category,description:" +msgid "Description" +msgstr "Beschreibung" + +msgctxt "field:cashbook.category,account:" +msgid "Account" +msgstr "Konto" + +msgctxt "field:cashbook.category,account_code:" +msgid "Account" +msgstr "Konto" + +msgctxt "field:cashbook.category,company:" +msgid "Company" +msgstr "Unternehmen" + +msgctxt "field:cashbook.category,parent:" +msgid "Parent" +msgstr "Übergeordnet" + +msgctxt "field:cashbook.category,childs:" +msgid "Children" +msgstr "Untergeordnet" + +msgctxt "field:cashbook.category,left:" +msgid "Left" +msgstr "Links" + +msgctxt "field:cashbook.category,right:" +msgid "Right" +msgstr "Rechts" + + ############################# # cashbook.open_lines.start # ############################# @@ -315,6 +390,10 @@ msgctxt "field:cashbook.open_lines.start,date_to:" msgid "End Date" msgstr "Endedatum" +msgctxt "field:cashbook.open_lines.start,num_books:" +msgid "Number of Cashbooks" +msgstr "Anzahl Kassenbücher" + ####################### # cashbook.open_lines # @@ -366,3 +445,55 @@ msgstr "Beginndatum" msgctxt "field:cashbook.line.context,date_to:" msgid "End Date" msgstr "Endedatum" + + +########################## +# cashbook.configuration # +########################## +msgctxt "model:cashbook.configuration,name:" +msgid "Configuration" +msgstr "Konfiguration" + +msgctxt "view:cashbook.configuration:" +msgid "Open Cashbook Wizard" +msgstr "Dialog: Kassenbuch öffnen" + +msgctxt "field:cashbook.configuration,date_from:" +msgid "Start Date" +msgstr "Beginndatum" + +msgctxt "field:cashbook.configuration,date_to:" +msgid "End Date" +msgstr "Endedatum" + +msgctxt "field:cashbook.configuration,checked:" +msgid "Checked" +msgstr "Geprüft" + +msgctxt "field:cashbook.configuration,done:" +msgid "Done" +msgstr "Fertig" + + +############################### +# cashbook.configuration_user # +############################### +msgctxt "model:cashbook.configuration_user,name:" +msgid "User Configuration" +msgstr "Nutzer Konfiguration" + +msgctxt "field:cashbook.configuration_user,date_from:" +msgid "Start Date" +msgstr "Beginndatum" + +msgctxt "field:cashbook.configuration_user,date_to:" +msgid "End Date" +msgstr "Endedatum" + +msgctxt "field:cashbook.configuration_user,checked:" +msgid "Checked" +msgstr "Geprüft" + +msgctxt "field:cashbook.configuration_user,done:" +msgid "Done" +msgstr "Fertig" diff --git a/locale/en.po b/locale/en.po new file mode 100644 index 0000000..c2d2075 --- /dev/null +++ b/locale/en.po @@ -0,0 +1,368 @@ +# +msgid "" +msgstr "Content-Type: text/plain; charset=utf-8\n" + +msgctxt "model:ir.message,text:msg_type_short_unique" +msgid "The Abbreviation must be unique." +msgstr "The Abbreviation must be unique." + +msgctxt "model:ir.message,text:msg_name_cashbook" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "model:ir.message,text:msg_line_wrong_state_value" +msgid "Invalid value of the field 'Status', allowed: edit, check, done." +msgstr "Invalid value of the field 'Status', allowed: edit, check, done." + +msgctxt "model:ir.message,text:msg_book_wrong_state_value" +msgid "Invalid value of the field 'Status', allowed: open, closed, archive." +msgstr "Invalid value of the field 'Status', allowed: open, closed, archive." + +msgctxt "model:ir.message,text:msg_book_deny_delete" +msgid "The cashbook '%(bookname)s' cannot be deleted because it contains %(booklines)s lines and is not in the status 'Archive'." +msgstr "The cashbook '%(bookname)s' cannot be deleted because it contains %(booklines)s lines and is not in the status 'Archive'." + +msgctxt "model:ir.message,text:msg_book_deny_write" +msgid "The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed." +msgstr "The cash book '%(bookname)s' is '%(state_txt)s' and cannot be changed." + +msgctxt "model:ir.message,text:msg_line_deny_delete1" +msgid "The cashbook line '%(linetxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'." +msgstr "The cashbook line '%(linetxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'." + +msgctxt "model:ir.message,text:msg_line_deny_delete2" +msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'." +msgstr "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'." + +msgctxt "model:ir.message,text:msg_setting_already_exists" +msgid "Settings for this user alredy exists." +msgstr "Settings for this user alredy exists." + +msgctxt "model:res.group,name:group_cashbook" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "model:res.group,name:group_cashbook_checkline" +msgid "Cashbook - WF - Check" +msgstr "Cashbook - WF - Check" + +msgctxt "model:res.group,name:group_cashbook_doneline" +msgid "Cashbook - WF - Done" +msgstr "Cashbook - WF - Done" + +msgctxt "model:ir.rule.group,name:rg_book_read" +msgid "Owners, observers and reviewers: Cashbook read" +msgstr "Owners, observers and reviewers: Cashbook read" + +msgctxt "model:ir.rule.group,name:rg_book_write_adm" +msgid "Administrators: Cashbook read/write" +msgstr "Administrators: Cashbook read/write" + +msgctxt "model:ir.rule.group,name:rg_line_write_adm" +msgid "Administrators: Cashbook line read/write" +msgstr "Administrators: Cashbook line read/write" + +msgctxt "model:ir.rule.group,name:rg_line_write" +msgid "Owners and reviewers: Cashbook line write" +msgstr "Owners and reviewers: Cashbook line write" + +msgctxt "model:ir.rule.group,name:rg_line_read" +msgid "Observer: Cashbook line read" +msgstr "Observer: Cashbook line read" + +msgctxt "model:ir.ui.menu,name:menu_cashbook" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "model:ir.ui.menu,name:menu_config" +msgid "Configuration" +msgstr "Configuration" + +msgctxt "model:ir.ui.menu,name:menu_typeconfig" +msgid "Cashbook Type" +msgstr "Cashbook Type" + +msgctxt "model:ir.ui.menu,name:menu_bookconfig" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "model:ir.ui.menu,name:menu_open_lines" +msgid "Open Cashbook" +msgstr "Open Cashbook" + +msgctxt "model:ir.action,name:act_book_view" +msgid "Account" +msgstr "Account" + +msgctxt "model:ir.action,name:act_type_view" +msgid "Cashbook Type" +msgstr "Cashbook Type" + +msgctxt "model:ir.action,name:act_line_view" +msgid "Cashbook Line" +msgstr "Cashbook Line" + +msgctxt "model:ir.action,name:act_open_lines" +msgid "Open Cashbook" +msgstr "Open Cashbook" + +msgctxt "model:ir.model.button,string:line_wfedit_button" +msgid "Edit" +msgstr "Edit" + +msgctxt "model:ir.model.button,string:line_wfcheck_button" +msgid "Check" +msgstr "Check" + +msgctxt "model:ir.model.button,string:line_wfdone_button" +msgid "Done" +msgstr "Done" + +msgctxt "model:ir.model.button,string:book_wfopen_button" +msgid "Open" +msgstr "Open" + +msgctxt "model:ir.model.button,string:book_wfclosed_button" +msgid "Close" +msgstr "Close" + +msgctxt "model:ir.model.button,string:book_wfarchive_button" +msgid "Archive" +msgstr "Archive" + +msgctxt "model:cashbook.book,name:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "view:cashbook.book:" +msgid "Owner & Authorizeds" +msgstr "Owner & Authorizeds" + +msgctxt "field:cashbook.book,name:" +msgid "Name" +msgstr "Name" + +msgctxt "field:cashbook.book,btype:" +msgid "Type" +msgstr "Type" + +msgctxt "field:cashbook.book,state:" +msgid "State" +msgstr "State" + +msgctxt "selection:cashbook.book,state:" +msgid "Open" +msgstr "Open" + +msgctxt "selection:cashbook.book,state:" +msgid "Closed" +msgstr "Closed" + +msgctxt "selection:cashbook.book,state:" +msgid "Archive" +msgstr "Archive" + +msgctxt "field:cashbook.book,owner:" +msgid "Owner" +msgstr "Owner" + +msgctxt "field:cashbook.book,reviewer:" +msgid "Reviewer" +msgstr "Reviewer" + +msgctxt "help:cashbook.book,reviewer:" +msgid "Group of users who have write access to the cashbook." +msgstr "Group of users who have write access to the cashbook." + +msgctxt "field:cashbook.book,observer:" +msgid "Observer" +msgstr "Observer" + +msgctxt "help:cashbook.book,observer:" +msgid "Group of users who have read-only access to the cashbook." +msgstr "Group of users who have read-only access to the cashbook." + +msgctxt "model:cashbook.line,name:" +msgid "Cashbook Line" +msgstr "Cashbook Line" + +msgctxt "view:cashbook.line:" +msgid "Cashbook Line" +msgstr "Cashbook Line" + +msgctxt "view:cashbook.line:" +msgid "State" +msgstr "State" + +msgctxt "field:cashbook.line,cashbook:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "field:cashbook.line,date:" +msgid "Date" +msgstr "Date" + +msgctxt "field:cashbook.line,description:" +msgid "Description" +msgstr "Description" + +msgctxt "field:cashbook.line,state:" +msgid "State" +msgstr "State" + +msgctxt "selection:cashbook.line,state:" +msgid "Edit" +msgstr "Edit" + +msgctxt "selection:cashbook.line,state:" +msgid "Checked" +msgstr "Checked" + +msgctxt "selection:cashbook.line,state:" +msgid "Done" +msgstr "Done" + +msgctxt "model:cashbook.type,name:" +msgid "Cashbook Type" +msgstr "Cashbook Type" + +msgctxt "field:cashbook.type,name:" +msgid "Name" +msgstr "Name" + +msgctxt "field:cashbook.type,short:" +msgid "Abbreviation" +msgstr "Abbreviation" + +msgctxt "model:cashbook.type,name:atype_cash" +msgid "Cash" +msgstr "Cash" + +msgctxt "model:cashbook.type,name:atype_giro" +msgid "Giro" +msgstr "Giro" + +msgctxt "model:cashbook.type,name:atype_fixtermdep" +msgid "Fixed-term deposit" +msgstr "Fixed-term deposit" + +msgctxt "model:cashbook.open_lines.start,name:" +msgid "Open Cashbook" +msgstr "Open Cashbook" + +msgctxt "field:cashbook.open_lines.start,cashbook:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "field:cashbook.open_lines.start,checked:" +msgid "Checked" +msgstr "Checked" + +msgctxt "help:cashbook.open_lines.start,checked:" +msgid "Show cashbook lines in Checked-state." +msgstr "Show cashbook lines in Checked-state." + +msgctxt "field:cashbook.open_lines.start,done:" +msgid "Done" +msgstr "Done" + +msgctxt "help:cashbook.open_lines.start,done:" +msgid "Show cashbook lines in Done-state." +msgstr "Show cashbook lines in Done-state." + +msgctxt "field:cashbook.open_lines.start,date_from:" +msgid "Start Date" +msgstr "Start Date" + +msgctxt "field:cashbook.open_lines.start,date_to:" +msgid "End Date" +msgstr "End Date" + +msgctxt "field:cashbook.open_lines.start,num_books:" +msgid "Number of Cashbooks" +msgstr "Number of Cashbooks" + +msgctxt "model:cashbook.open_lines,name:" +msgid "Open Cashbook" +msgstr "Open Cashbook" + +msgctxt "wizard_button:cashbook.open_lines,start,end:" +msgid "Cancel" +msgstr "Cancel" + +msgctxt "wizard_button:cashbook.open_lines,start,open_:" +msgid "Open" +msgstr "Open" + +msgctxt "model:cashbook.line.context,name:" +msgid "Line Context" +msgstr "Line Context" + +msgctxt "field:cashbook.line.context,cashbook:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "field:cashbook.line.context,checked:" +msgid "Checked" +msgstr "Checked" + +msgctxt "help:cashbook.line.context,checked:" +msgid "Show cashbook lines in Checked-state." +msgstr "Show cashbook lines in Checked-state." + +msgctxt "field:cashbook.line.context,done:" +msgid "Done" +msgstr "Done" + +msgctxt "help:cashbook.line.context,done:" +msgid "Show cashbook lines in Done-state." +msgstr "Show cashbook lines in Done-state." + +msgctxt "field:cashbook.line.context,date_from:" +msgid "Start Date" +msgstr "Start Date" + +msgctxt "field:cashbook.line.context,date_to:" +msgid "End Date" +msgstr "End Date" + +msgctxt "model:cashbook.configuration,name:" +msgid "Configuration" +msgstr "Configuration" + +msgctxt "view:cashbook.configuration:" +msgid "Open Cashbook Wizard" +msgstr "Open Cashbook Wizard" + +msgctxt "field:cashbook.configuration,date_from:" +msgid "Start Date" +msgstr "Start Date" + +msgctxt "field:cashbook.configuration,date_to:" +msgid "End Date" +msgstr "End Date" + +msgctxt "field:cashbook.configuration,checked:" +msgid "Checked" +msgstr "Checked" + +msgctxt "field:cashbook.configuration,done:" +msgid "Done" +msgstr "Done" + +msgctxt "model:cashbook.configuration_user,name:" +msgid "User Configuration" +msgstr "User Configuration" + +msgctxt "field:cashbook.configuration_user,date_from:" +msgid "Start Date" +msgstr "Start Date" + +msgctxt "field:cashbook.configuration_user,date_to:" +msgid "End Date" +msgstr "End Date" + +msgctxt "field:cashbook.configuration_user,checked:" +msgid "Checked" +msgstr "Checked" + diff --git a/menu.xml b/menu.xml index ea8ebd3..90524d3 100644 --- a/menu.xml +++ b/menu.xml @@ -17,8 +17,8 @@ full copyright notices and license terms. --> - - + @@ -44,6 +44,23 @@ full copyright notices and license terms. --> + + + + + + + + + + + + + The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'. + + Settings for this user alredy exists. + + + The category name already exists at this level. + diff --git a/model.py b/model.py new file mode 100644 index 0000000..d2124f3 --- /dev/null +++ b/model.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# 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. + +from trytond.model import MultiValueMixin, ValueMixin, fields, Unique +from trytond.transaction import Transaction + + +class UserValueMixin(ValueMixin): + iduser = fields.Many2One(model_name='res.user', string="User", + select=True, ondelete='CASCADE', required=True) + + @classmethod + def __setup__(cls): + super(UserValueMixin, cls).__setup__() + tab_val = cls.__table__() + cls._sql_constraints.extend([ + ('val_uniq', + Unique(tab_val, tab_val.iduser), + 'cashbook.msg_setting_already_exists'), + ]) + +# end UserValueMixin + + +class UserMultiValueMixin(MultiValueMixin): + + def updt_multivalue_pattern(self, pattern): + """ add values to pattern + """ + pattern.setdefault('iduser', Transaction().user) + return pattern + + def get_multivalue(self, name, **pattern): + Value = self.multivalue_model(name) + if issubclass(Value, UserValueMixin): + pattern = self.updt_multivalue_pattern(pattern) + return super(UserMultiValueMixin, self).get_multivalue(name, **pattern) + + def set_multivalue(self, name, value, **pattern): + Value = self.multivalue_model(name) + if issubclass(Value, UserValueMixin): + pattern = self.updt_multivalue_pattern(pattern) + return super(UserMultiValueMixin, self).set_multivalue(name, value, **pattern) + +# end UserMultiValueMixin diff --git a/tests/__init__.py b/tests/__init__.py index e77df97..25789bd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -7,11 +7,15 @@ import unittest from trytond.modules.cashbook.tests.test_type import TypeTestCase from trytond.modules.cashbook.tests.test_book import BookTestCase from trytond.modules.cashbook.tests.test_line import LineTestCase +from trytond.modules.cashbook.tests.test_config import ConfigTestCase +from trytond.modules.cashbook.tests.test_category import CategoryTestCase __all__ = ['suite'] class CashbookTestCase(\ + CategoryTestCase,\ + ConfigTestCase,\ LineTestCase, BookTestCase, TypeTestCase, diff --git a/tests/test_book.py b/tests/test_book.py index a7eaf06..11259f7 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -1,7 +1,8 @@ -# This file is part of Tryton. The COPYRIGHT file at the top level of -# this repository contains the full copyright notices and license terms. +# -*- coding: utf-8 -*- +# 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. -import os, requests from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.pool import Pool from trytond.transaction import Transaction @@ -54,7 +55,7 @@ class BookTestCase(ModuleTestCase): self.assertEqual(book.state, 'open') self.assertRaisesRegex(UserError, - "The cash book 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.", + "The cashbook 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.", Book.delete, [book]) @@ -82,7 +83,7 @@ class BookTestCase(ModuleTestCase): self.assertEqual(book.state, 'closed') self.assertRaisesRegex(UserError, - "The cash book 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.", + "The cashbook 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.", Book.delete, [book]) diff --git a/tests/test_category.py b/tests/test_category.py new file mode 100644 index 0000000..03a4616 --- /dev/null +++ b/tests/test_category.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# 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. + +from trytond.tests.test_tryton import ModuleTestCase, with_transaction +from trytond.pool import Pool +from trytond.transaction import Transaction +from trytond.exceptions import UserError +from trytond.modules.company.tests import create_company + + +class CategoryTestCase(ModuleTestCase): + 'Test cashbook categoy module' + module = 'cashbook' + + @with_transaction() + def test_category_create_nodupl_at_root(self): + """ create category, duplicates are allowed at root-level + """ + pool = Pool() + Category = pool.get('cashbook.category') + + company = create_company(name='m-ds') + + with Transaction().set_context({ + 'company': company.id, + }): + cat1, = Category.create([{ + 'name': 'Test 1', + 'description': 'Info', + }]) + self.assertEqual(cat1.name, 'Test 1') + self.assertEqual(cat1.rec_name, 'Test 1') + self.assertEqual(cat1.description, 'Info') + self.assertEqual(cat1.company.rec_name, 'm-ds') + self.assertEqual(cat1.parent, None) + + # duplicate, allowed + cat2, = Category.create([{ + 'name': 'Test 1', + 'description': 'Info', + }]) + self.assertEqual(cat2.name, 'Test 1') + self.assertEqual(cat2.rec_name, 'Test 1') + self.assertEqual(cat2.description, 'Info') + self.assertEqual(cat2.company.rec_name, 'm-ds') + self.assertEqual(cat2.parent, None) + + @with_transaction() + def test_category_create_nodupl_diff_level(self): + """ create category + """ + pool = Pool() + Category = pool.get('cashbook.category') + + company = create_company(name='m-ds') + + with Transaction().set_context({ + 'company': company.id, + }): + cat1, = Category.create([{ + 'name': 'Test 1', + 'description': 'Info', + 'childs': [('create', [{ + 'name': 'Test 1', + }])], + }]) + self.assertEqual(cat1.name, 'Test 1') + self.assertEqual(cat1.rec_name, 'Test 1') + self.assertEqual(cat1.description, 'Info') + self.assertEqual(cat1.company.rec_name, 'm-ds') + + self.assertEqual(len(cat1.childs), 1) + self.assertEqual(cat1.childs[0].rec_name, 'Test 1/Test 1') + + @with_transaction() + def test_category_create_deny_dupl_at_sublevel(self): + """ create category + """ + pool = Pool() + Category = pool.get('cashbook.category') + + company = create_company(name='m-ds') + + with Transaction().set_context({ + 'company': company.id, + }): + self.assertRaisesRegex(UserError, + 'The category name already exists at this level.', + Category.create, + [{ + 'name': 'Test 1', + 'description': 'Info', + 'childs': [('create', [{ + 'name': 'Test 1', + }, { + 'name': 'Test 1', + }])], + }]) + + @with_transaction() + def test_category_create_with_account(self): + """ create category + account + """ + pool = Pool() + Account = pool.get('account.account') + Category = pool.get('cashbook.category') + + company = create_company(name='m-ds') + + with Transaction().set_context({ + 'company': company.id, + }): + account, = Account.create([{ + 'name': 'Account No 1', + 'code': '0123', + }]) + + cat1, = Category.create([{ + 'name': 'Test 1', + 'description': 'Info', + 'account': account.id, + }]) + self.assertEqual(cat1.name, 'Test 1') + self.assertEqual(cat1.rec_name, '0123 Test 1') + self.assertEqual(cat1.description, 'Info') + self.assertEqual(cat1.company.rec_name, 'm-ds') + + self.assertEqual(Category.search_count([ + ('account_code', '=', '0123'), + ]), 1) + self.assertEqual(Category.search_count([ + ('account_code', '=', '123'), + ]), 0) + +# end CategoryTestCase diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..4e0ec0e --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# 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. + +from trytond.tests.test_tryton import ModuleTestCase, with_transaction +from trytond.pool import Pool +from trytond.transaction import Transaction +from trytond.exceptions import UserError +from datetime import date + + +class ConfigTestCase(ModuleTestCase): + 'Test config type module' + module = 'cashbook' + + @with_transaction() + def test_config_create(self): + """ create config + """ + Configuration = Pool().get('cashbook.configuration') + + cfg1 = Configuration() + cfg1.save() + + cfg2 = Configuration.get_singleton() + self.assertEqual(cfg2.date_from, None) + self.assertEqual(cfg2.date_to, None) + self.assertEqual(cfg2.checked, True) + self.assertEqual(cfg2.done, False) + + @with_transaction() + def test_config_create_multi_user(self): + """ create config, multi-user + """ + pool = Pool() + Configuration = pool.get('cashbook.configuration') + ResUser = pool.get('res.user') + + usr_lst = ResUser.create([{ + 'login': 'frida', + 'name': 'Frida', + }, { + 'login': 'diego', + 'name': 'Diego', + }]) + self.assertEqual(len(usr_lst), 2) + self.assertEqual(usr_lst[0].name, 'Frida') + self.assertEqual(usr_lst[1].name, 'Diego') + + with Transaction().set_context({ + '_check_access': True, + }): + # change to user 'frida' + with Transaction().set_user(usr_lst[0].id): + cfg1 = Configuration() + cfg1.save() + + cfg2 = Configuration.get_singleton() + self.assertEqual(cfg2.date_from, None) + self.assertEqual(cfg2.date_to, None) + self.assertEqual(cfg2.checked, True) + self.assertEqual(cfg2.done, False) + + cfg2.date_from = date(2022, 4, 1) + cfg2.date_to = date(2022, 5, 30) + cfg2.checked = False + cfg2.save() + + # change to user 'diego' + with Transaction().set_user(usr_lst[1].id): + cfg1 = Configuration() + cfg1.save() + + cfg2 = Configuration.get_singleton() + self.assertEqual(cfg2.date_from, None) + self.assertEqual(cfg2.date_to, None) + self.assertEqual(cfg2.checked, True) + self.assertEqual(cfg2.done, False) + + cfg2.date_from = date(2022, 4, 15) + cfg2.date_to = date(2022, 5, 15) + cfg2.save() + + # change to user 'frida' - check + with Transaction().set_user(usr_lst[0].id): + cfg1 = Configuration() + cfg1.save() + + cfg2 = Configuration.get_singleton() + self.assertEqual(cfg2.date_from, date(2022, 4, 1)) + self.assertEqual(cfg2.date_to, date(2022, 5, 30)) + self.assertEqual(cfg2.checked, False) + self.assertEqual(cfg2.done, False) + +# end ConfigTestCase diff --git a/tests/test_line.py b/tests/test_line.py index 85ba07c..7cbaf6a 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -1,7 +1,8 @@ -# This file is part of Tryton. The COPYRIGHT file at the top level of -# this repository contains the full copyright notices and license terms. +# -*- coding: utf-8 -*- +# 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. -import os, requests from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.pool import Pool from trytond.transaction import Transaction diff --git a/tests/test_type.py b/tests/test_type.py index f2edb95..9e8544a 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -1,7 +1,8 @@ -# This file is part of Tryton. The COPYRIGHT file at the top level of -# this repository contains the full copyright notices and license terms. +# -*- coding: utf-8 -*- +# 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. -import os, requests from trytond.tests.test_tryton import ModuleTestCase, with_transaction from trytond.pool import Pool from trytond.transaction import Transaction diff --git a/tryton.cfg b/tryton.cfg index 1788106..62d0ac8 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -2,11 +2,14 @@ version=6.0.0 depends: res + account xml: icon.xml group.xml message.xml types.xml + category.xml + configuration.xml book.xml line.xml wizard_openline.xml diff --git a/types.py b/types.py index f6309d0..c8d76dd 100644 --- a/types.py +++ b/types.py @@ -18,9 +18,9 @@ class Type(ModelSQL, ModelView): super(Type, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) t = cls.__table__() - cls._sql_constraints = [ + cls._sql_constraints.extend([ ('code_uniq', Unique(t, t.short), 'cashbook.msg_type_short_unique'), - ] + ]) def get_rec_name(self, name): """ short + name @@ -32,7 +32,7 @@ class Type(ModelSQL, ModelView): @classmethod def search_rec_name(cls, name, clause): - """ search in tracker + ticket-name + """ search in name + short """ return ['OR', ('name',) + tuple(clause[1:]), diff --git a/view/book_form.xml b/view/book_form.xml index 5035ecb..1a20cdf 100644 --- a/view/book_form.xml +++ b/view/book_form.xml @@ -8,6 +8,10 @@ full copyright notices and license terms. -->