neu: kategorie, config + test

This commit is contained in:
Frederik Jaeckel 2022-08-09 15:08:41 +02:00
parent b9bb433c39
commit d6a8b254a3
28 changed files with 1255 additions and 35 deletions

View file

@ -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,

View file

@ -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')

85
category.py Normal file
View file

@ -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

91
category.xml Normal file
View file

@ -0,0 +1,91 @@
<?xml version="1.0"?>
<!-- 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. -->
<tryton>
<data>
<!-- views -->
<record model="ir.ui.view" id="category_view_list">
<field name="model">cashbook.category</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">category_list</field>
</record>
<record model="ir.ui.view" id="category_view_tree">
<field name="model">cashbook.category</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="field_childs">childs</field>
<field name="name">category_tree</field>
</record>
<record model="ir.ui.view" id="category_view_form">
<field name="model">cashbook.category</field>
<field name="type">form</field>
<field name="priority" eval="20"/>
<field name="name">category_form</field>
</record>
<!-- action view - list -->
<record model="ir.action.act_window" id="act_category_list">
<field name="name">Category</field>
<field name="res_model">cashbook.category</field>
</record>
<record model="ir.action.act_window.view" id="act_category_list-1">
<field name="sequence" eval="10"/>
<field name="view" ref="category_view_list"/>
<field name="act_window" ref="act_category_list"/>
</record>
<record model="ir.action.act_window.view" id="act_category_list-2">
<field name="sequence" eval="20"/>
<field name="view" ref="category_view_form"/>
<field name="act_window" ref="act_category_list"/>
</record>
<!-- action view - tree -->
<record model="ir.action.act_window" id="act_category_tree">
<field name="name">Category</field>
<field name="res_model">cashbook.category</field>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_category_tree-1">
<field name="sequence" eval="10"/>
<field name="view" ref="category_view_tree"/>
<field name="act_window" ref="act_category_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_category_tree-2">
<field name="sequence" eval="20"/>
<field name="view" ref="category_view_form"/>
<field name="act_window" ref="act_category_tree"/>
</record>
<!-- permission -->
<!-- anon: deny all -->
<record model="ir.model.access" id="access_category-anon">
<field name="model" search="[('model', '=', 'cashbook.category')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<!-- admin: read/write -->
<record model="ir.model.access" id="access_category-group_admin">
<field name="model" search="[('model', '=', 'cashbook.category')]"/>
<field name="group" ref="res.group_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- cashbook: read -->
<record model="ir.model.access" id="access_category-group_cashbook">
<field name="model" search="[('model', '=', 'cashbook.category')]"/>
<field name="group" ref="group_cashbook"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
</data>
</tryton>

79
configuration.py Normal file
View file

@ -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

24
configuration.xml Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<!-- 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. -->
<tryton>
<data>
<record model="ir.ui.view" id="configuration_view_form">
<field name="model">cashbook.configuration</field>
<field name="type">form</field>
<field name="name">configuration_form</field>
</record>
<record model="ir.action.act_window" id="act_configuration_form">
<field name="name">Configuration</field>
<field name="res_model">cashbook.configuration</field>
</record>
<record model="ir.action.act_window.view" id="act_configuration_view1">
<field name="sequence" eval="1"/>
<field name="view" ref="configuration_view_form"/>
<field name="act_window" ref="act_configuration_form"/>
</record>
</data>
</tryton>

19
line.py
View file

@ -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__()

View file

@ -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"

368
locale/en.po Normal file
View file

@ -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"

View file

@ -17,8 +17,8 @@ full copyright notices and license terms. -->
<field name="group" ref="group_cashbook"/>
</record>
<!-- menu: /Cashbook/Configuration -->
<menuitem id="menu_config" name="Configuration"
<!-- menu: /Cashbook/Configuration name="Configuration" -->
<menuitem id="menu_config" action="act_configuration_form"
icon="tryton-list"
parent="menu_cashbook" sequence="10"/>
<record model="ir.ui.menu-res.group" id="menu_config-group_admin">
@ -44,6 +44,23 @@ full copyright notices and license terms. -->
<field name="group" ref="res.group_admin"/>
</record>
<!-- menu: /Cashbook/Configuration/Category -->
<menuitem id="menu_category" action="act_category_tree"
icon="tryton-tree"
parent="menu_config" sequence="30"/>
<record model="ir.ui.menu-res.group" id="menu_category-group_admin">
<field name="menu" ref="menu_category"/>
<field name="group" ref="res.group_admin"/>
</record>
<!-- menu: /Cashbook/Configuration/Category/Category -->
<menuitem id="menu_category_list" action="act_category_list"
icon="tryton-tree"
parent="menu_category" sequence="30"/>
<record model="ir.ui.menu-res.group" id="menu_category_list-group_admin">
<field name="menu" ref="menu_category_list"/>
<field name="group" ref="res.group_admin"/>
</record>
<!-- menu: /Cashbook/Open Cashbook -->
<menuitem id="menu_open_lines" action="act_open_lines"
icon="tryton-tree"

View file

@ -29,6 +29,12 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_line_deny_delete2">
<field name="text">The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'.</field>
</record>
<record model="ir.message" id="msg_setting_already_exists">
<field name="text">Settings for this user alredy exists.</field>
</record>
<record model="ir.message" id="msg_category_name_unique">
<field name="text">The category name already exists at this level.</field>
</record>
</data>
</tryton>

47
model.py Normal file
View file

@ -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

View file

@ -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,

View file

@ -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

137
tests/test_category.py Normal file
View file

@ -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

96
tests/test_config.py Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:]),

View file

@ -8,6 +8,10 @@ full copyright notices and license terms. -->
<label name="btype"/>
<field name="btype"/>
<label name="account"/>
<field name="account"/>
<newline/>
<label name="state"/>
<field name="state"/>
<group id="grpstate" col="3" colspan="2">

View file

@ -5,6 +5,7 @@ full copyright notices and license terms. -->
<tree>
<field name="name"/>
<field name="btype"/>
<field name="account"/>
<field name="owner"/>
<field name="reviewer"/>
<field name="observer"/>

View file

@ -15,5 +15,7 @@ full copyright notices and license terms. -->
<field name="checked"/>
<label name="done"/>
<field name="done"/>
<field name="num_cashbook"/>
</form>

24
view/category_form.xml Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<!-- 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. -->
<form>
<label name="name"/>
<field name="name"/>
<label name="account"/>
<field name="account"/>
<label name="description"/>
<field name="description" colspan="3"/>
<notebook colspan="6">
<page string="General Information" id="general" col="6">
<label name="company"/>
<field name="company"/>
<label name="parent"/>
<field name="parent"/>
<field name="childs" colspan="4"/>
</page>
</notebook>
</form>

8
view/category_list.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<!-- 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. -->
<tree>
<field name="rec_name"/>
<field name="account"/>
</tree>

10
view/category_tree.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!-- 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. -->
<tree>
<field name="name"/>
<field name="account_code"/>
<field name="parent" tree_invisible="1"/>
<field name="childs" tree_invisible="1"/>
</tree>

View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<separator id="sepwiz" colspan="4" string="Open Cashbook Wizard"/>
<label name="date_from"/>
<field name="date_from"/>
<label name="date_to"/>
<field name="date_to"/>
<label name="checked"/>
<field name="checked"/>
<label name="done"/>
<field name="done"/>
</form>

View file

@ -5,9 +5,11 @@
from trytond.model import ModelView, ModelSQL, fields
from trytond.pyson import PYSONEncoder
from trytond.wizard import Wizard, StateView, StateAction, Button
from trytond.wizard import Wizard, StateView, StateTransition, StateAction, Button
from trytond.i18n import gettext
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
class OpenCashBookStart(ModelView):
@ -36,38 +38,81 @@ class OpenCashBook(Wizard):
'Open Cashbook'
__name__ = 'cashbook.open_lines'
start = StateView('cashbook.open_lines.start',
start_state = 'check'
check = StateTransition()
askuser = StateView('cashbook.open_lines.start',
'cashbook.open_lines_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Open', 'open_', 'tryton-ok', default=True),
])
open_ = StateAction('cashbook.act_line_view')
def default_start(self, fields):
""" setup form
def transition_check(self):
""" dont ask and open cashbook if user has 1x only
"""
Book = Pool().get('cashbook.book')
result = {}
with Transaction().set_context({
'_check_access': True,
}):
books = Book.search([])
print('\n## books:', books)
if len(books) == 1:
result['cashbook'] = books[0].id
return 'open_'
return 'askuser'
def default_askuser(self, fields):
""" setup form
"""
Configuration = Pool().get('cashbook.configuration')
cfg1 = Configuration.get_singleton()
result = {
'date_from': getattr(cfg1, 'date_from', None),
'date_to': getattr(cfg1, 'date_to', None),
'checked': getattr(cfg1, 'checked', True),
'done': getattr(cfg1, 'done', False),
}
return result
def do_open_(self, action):
""" open view, use data from dialog
"""
pool = Pool()
Book = pool.get('cashbook.book')
Configuration = pool.get('cashbook.configuration')
cfg1 = Configuration.get_singleton()
book = getattr(self.askuser, 'cashbook', None)
if book is None:
with Transaction().set_context({
'_check_access': True,
}):
books = Book.search([])
if len(books) > 0:
book = books[0]
date_from = getattr(self.askuser, 'date_from', None)
date_to = getattr(self.askuser, 'date_to', None)
checked = getattr(self.askuser, 'checked', True)
done = getattr(self.askuser, 'done', False)
# save settings
cfg1.date_from = date_from
cfg1.date_to = date_to
cfg1.checked = checked
cfg1.done = done
cfg1.save()
action['pyson_context'] = PYSONEncoder().encode({
'cashbook': self.start.cashbook.id,
'date_from': self.start.date_from,
'date_to': self.start.date_to,
'checked': self.start.checked,
'done': self.start.done,
'cashbook': getattr(book, 'id', None),
'date_from': date_from,
'date_to': date_to,
'checked': checked,
'done': done,
})
action['name'] = '%(name)s: %(cashbook)s' % {
'name': gettext('cashbook.msg_name_cashbook'),
'cashbook': self.start.cashbook.rec_name,
'cashbook': getattr(book, 'rec_name', '-/-'),
}
return action, {}