book, line, category, types: berechtigung für company-user + test,

book: währung neu,
line: buchungstyp, betrag, credit, debit, währung, sortierung
This commit is contained in:
Frederik Jaeckel 2022-08-10 16:30:08 +02:00
parent 1a85b8e80e
commit d57d76ba3b
20 changed files with 620 additions and 115 deletions

29
book.py
View file

@ -4,10 +4,11 @@
# full copyright notices and license terms.
from trytond.model import Workflow, ModelView, ModelSQL, fields, Check
from trytond.pyson import Eval
from trytond.pyson import Eval, Or, Bool
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.transaction import Transaction
from trytond.pool import Pool
STATES = {
@ -26,6 +27,8 @@ class Book(Workflow, ModelSQL, ModelView):
'Cashbook'
__name__ = 'cashbook.book'
company = fields.Many2One(string='Company', model_name='company.company',
required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True,
states=STATES, depends=DEPENDS)
btype = fields.Many2One(string='Type', required=True,
@ -45,6 +48,14 @@ class Book(Workflow, ModelSQL, ModelView):
account = fields.Many2One(string='Account', select=True,
model_name='account.account', ondelete='RESTRICT',
states=STATES, depends=DEPENDS)
currency = fields.Many2One(string='Currency', required=True,
model_name='currency.currency',
states={
'readonly': Or(
STATES['readonly'],
Bool(Eval('lines', [])),
),
}, depends=DEPENDS+['lines'])
state = fields.Selection(string='State', required=True,
readonly=True, selection=sel_state_book)
state_string = state.translated('state')
@ -79,6 +90,22 @@ class Book(Workflow, ModelSQL, ModelView):
},
})
@classmethod
def default_currency(cls):
""" currency of company
"""
Company = Pool().get('company.company')
company = cls.default_company()
if company:
company = Company(company)
if company.currency:
return company.currency.id
@staticmethod
def default_company():
return Transaction().context.get('company') or None
@classmethod
def default_state(cls):
return 'open'

View file

@ -107,6 +107,19 @@ full copyright notices and license terms. -->
<field name="group" ref="group_cashbook"/>
</record>
<record model="ir.rule.group" id="rg_book_companies">
<field name="name">User in companies</field>
<field name="model"
search="[('model', '=', 'cashbook.book')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="r_book_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rg_book_companies"/>
</record>
<!-- button - open -->
<record model="ir.model.button" id="book_wfopen_button">
<field name="name">wfopen</field>

View file

@ -87,5 +87,18 @@ full copyright notices and license terms. -->
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.rule.group" id="rg_category_companies">
<field name="name">User in companies</field>
<field name="model"
search="[('model', '=', 'cashbook.category')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="r_category_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rg_category_companies"/>
</record>
</data>
</tryton>

112
line.py
View file

@ -10,8 +10,10 @@ from trytond.transaction import Transaction
from trytond.report import Report
from trytond.exceptions import UserError
from trytond.i18n import gettext
from decimal import Decimal
from sql import Cast, Literal
from sql.functions import DatePart
from sql.conditionals import Case
from .book import sel_state_book
@ -21,6 +23,13 @@ sel_linetype = [
('done', 'Done'),
]
sel_bookingtype = [
('in', 'Revenue'),
('out', 'Expense'),
('mvin', 'Transfer from'),
('mvout', 'Transfer to'),
]
STATES = {
'readonly': Or(
Eval('state', '') != 'edit',
@ -47,6 +56,21 @@ class Line(Workflow, ModelSQL, ModelView):
states=STATES, depends=DEPENDS)
category_view = fields.Function(fields.Char(string='Category', readonly=True),
'on_change_with_category_view', searcher='search_category_view')
bookingtype = fields.Selection(string='Type', required=True,
help='Type of Booking', selection=sel_bookingtype,
states=STATES, depends=DEPENDS)
amount = fields.Numeric(string='Amount', digits=(16, Eval('currency_digits', 2)),
required=True, states=STATES, depends=DEPENDS+['currency_digits'])
debit = fields.Numeric(string='Debit', digits=(16, Eval('currency_digits', 2)),
required=True, readonly=True, depends=['currency_digits'])
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
required=True, readonly=True, depends=['currency_digits'])
currency = fields.Function(fields.Many2One(model_name='currency.currency',
string="Currency"), 'on_change_with_currency')
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
'on_change_with_currency_digits')
state = fields.Selection(string='State', required=True, readonly=True,
select=True, selection=sel_linetype)
state_string = state.translated('state')
@ -59,6 +83,7 @@ class Line(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(Line, cls).__setup__()
cls._order.insert(0, ('state', 'ASC'))
cls._order.insert(0, ('date', 'ASC'))
t = cls.__table__()
cls._sql_constraints.extend([
@ -138,6 +163,23 @@ class Line(Workflow, ModelSQL, ModelView):
'desc': (self.description or '-')[:40],
}
@staticmethod
def order_state(tables):
""" edit = 0, check/done = 1
"""
Line = Pool().get('cashbook.line')
tab_line = Line.__table__()
table, _ = tables[None]
query = tab_line.select(
Case(
(tab_line.state == 'edit', 1),
(tab_line.state.in_(['check', 'done']), 0),
else_ = 2),
where=tab_line.id==table.id
)
return [query]
@staticmethod
def order_category_view(tables):
""" order: name
@ -219,11 +261,65 @@ class Line(Workflow, ModelSQL, ModelView):
"""
return [('cashbook.state',) + tuple(clause[1:])]
@fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency(self, name=None):
""" currency of cashbook
"""
if self.cashbook:
return self.cashbook.currency.id
@fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency_digits(self, name=None):
""" currency of cashbook
"""
if self.cashbook:
return self.cashbook.currency.digits
else:
return 2
@classmethod
def get_debit_credit(cls, values):
""" compute debit/credit from amount
"""
if isinstance(values, dict):
type_ = values.get('bookingtype', None)
amount = values.get('amount', None)
else :
type_ = getattr(values, 'bookingtype', None)
amount = getattr(values, 'amount', None)
if type_:
if amount is not None:
if type_ in ['in', 'mvin']:
return {
'debit': Decimal('0.0'),
'credit': amount,
}
elif type_ in ['out', 'mvout']:
return {
'debit': amount,
'credit': Decimal('0.0'),
}
else :
raise ValueError('invalid "bookingtype"')
return {}
@classmethod
def create(cls, vlist):
""" add debit/credit
"""
vlist = [x.copy() for x in vlist]
for vals in vlist:
vals.update(cls.get_debit_credit(vals))
return super(Line, cls).create(vlist)
@classmethod
def write(cls, *args):
""" deny update if cashbook.line!='open'
""" deny update if cashbook.line!='open',
add or update debit/credit
"""
actions = iter(args)
to_write = []
for lines, values in zip(actions, actions):
for line in lines:
if line.cashbook.state != 'open':
@ -232,7 +328,19 @@ class Line(Workflow, ModelSQL, ModelView):
bookname = line.cashbook.rec_name,
state_txt = line.cashbook.state_string,
))
super(Line, cls).write(*args)
# debit / credit
if len(set(values.keys()).intersection(set({'amount', 'bookingtype'}))) > 0:
for line in lines:
values2 = {}
values2.update(values)
values2.update(cls.get_debit_credit({
x:values.get(x, getattr(line, x)) for x in ['amount', 'bookingtype']
}))
to_write.extend([lines, values2])
else :
to_write.extend([lines, values])
super(Line, cls).write(*to_write)
@classmethod
def delete(cls, lines):

View file

@ -169,6 +169,19 @@ full copyright notices and license terms. -->
<field name="group" ref="group_cashbook"/>
</record>
<record model="ir.rule.group" id="rg_line_companies">
<field name="name">User in companies</field>
<field name="model"
search="[('model', '=', 'cashbook.line')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="r_line_companies">
<field name="domain"
eval="[('cashbook.company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rg_line_companies"/>
</record>
<!-- button - edit -->
<record model="ir.model.button" id="line_wfedit_button">

View file

@ -90,6 +90,18 @@ msgctxt "model:ir.rule.group,name:rg_line_read"
msgid "Observer: Cashbook line read"
msgstr "Beobachter: Kassenbuchzeile lesen"
msgctxt "model:ir.rule.group,name:rg_line_read"
msgid "User in companies"
msgstr "Benutzer im Unternehmen"
msgctxt "model:ir.rule.group,name:rg_type_companies"
msgid "User in companies"
msgstr "Benutzer im Unternehmen"
msgctxt "model:ir.rule.group,name:rg_book_companies"
msgid "User in companies"
msgstr "Benutzer im Unternehmen"
##############
# ir.ui.menu #
@ -127,8 +139,8 @@ msgstr "Kategorie"
# ir.action #
#############
msgctxt "model:ir.action,name:act_book_view"
msgid "Account"
msgstr "Konto"
msgid "Cashbook"
msgstr "Kassenbuch"
msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type"
@ -147,6 +159,22 @@ msgid "Category"
msgstr "Kategorie"
###############################
# ir.action.act_window.domain #
###############################
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_current"
msgid "Current Month"
msgstr "aktueller Monat"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
msgid "Last Month"
msgstr "letzter Monat"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
msgid "All"
msgstr "Alle"
###################
# ir.model.button #
###################
@ -183,8 +211,8 @@ msgid "Cashbook"
msgstr "Kassenbuch"
msgctxt "view:cashbook.book:"
msgid "Owner & Authorizeds"
msgstr "Eigentümer & Autorisierte"
msgid "Owner and Authorizeds"
msgstr "Eigentümer und Autorisierte"
msgctxt "field:cashbook.book,name:"
msgid "Name"
@ -234,6 +262,14 @@ msgctxt "field:cashbook.book,account:"
msgid "Account"
msgstr "Konto"
msgctxt "field:cashbook.book,company:"
msgid "Company"
msgstr "Unternehmen"
msgctxt "field:cashbook.book,currency:"
msgid "Currency"
msgstr "Währung"
#################
# cashbook.line #
@ -242,6 +278,14 @@ msgctxt "model:cashbook.line,name:"
msgid "Cashbook Line"
msgstr "Kassenbuchzeile"
msgctxt "view:cashbook.line:"
msgid "Credit"
msgstr "Einnahme"
msgctxt "view:cashbook.line:"
msgid "Debit"
msgstr "Ausgabe"
msgctxt "view:cashbook.line:"
msgid "Cashbook Line"
msgstr "Kassenbuchzeile"
@ -294,17 +338,53 @@ 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"
msgctxt "field:cashbook.line,bookingtype:"
msgid "Type"
msgstr "Typ"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_last"
msgid "Last Month"
msgstr "letzter Monat"
msgctxt "help:cashbook.line,bookingtype:"
msgid "Type of Booking"
msgstr "Typ der Buchung"
msgctxt "model:ir.action.act_window.domain,name:act_line_domain_all"
msgid "All"
msgstr "Alle"
msgctxt "selection:cashbook.line,bookingtype:"
msgid "Revenue"
msgstr "Einnahme"
msgctxt "selection:cashbook.line,bookingtype:"
msgid "Expense"
msgstr "Ausgabe"
msgctxt "selection:cashbook.line,bookingtype:"
msgid "Transfer from"
msgstr "Umbuchung von"
msgctxt "selection:cashbook.line,bookingtype:"
msgid "Transfer to"
msgstr "Umbuchung nach"
msgctxt "field:cashbook.line,company:"
msgid "Company"
msgstr "Unternehmen"
msgctxt "field:cashbook.line,amount:"
msgid "Amount"
msgstr "Betrag"
msgctxt "field:cashbook.line,debit:"
msgid "Debit"
msgstr "Ausgabe"
msgctxt "field:cashbook.line,credit:"
msgid "Credit"
msgstr "Einnahme"
msgctxt "field:cashbook.line,currency:"
msgid "Currency"
msgstr "Währung"
msgctxt "field:cashbook.line,currency_digits:"
msgid "Currency Digits"
msgstr "Nachkommastellen Währung"
#################
@ -322,17 +402,9 @@ msgctxt "field:cashbook.type,short:"
msgid "Abbreviation"
msgstr "Kürzel"
msgctxt "model:cashbook.type,name:atype_cash"
msgid "Cash"
msgstr "Bar"
msgctxt "model:cashbook.type,name:atype_giro"
msgid "Giro"
msgstr "Giro"
msgctxt "model:cashbook.type,name:atype_fixtermdep"
msgid "Fixed-term deposit"
msgstr "Festgeld"
msgctxt "field:cashbook.type,company:"
msgid "Company"
msgstr "Unternehmen"
#####################
@ -434,11 +506,11 @@ msgctxt "model:cashbook.open_lines,name:"
msgid "Open Cashbook"
msgstr "Kassenbuch öffnen"
msgctxt "wizard_button:cashbook.open_lines,start,end:"
msgctxt "wizard_button:cashbook.open_lines,askuser,end:"
msgid "Cancel"
msgstr "Abbruch"
msgctxt "wizard_button:cashbook.open_lines,start,open_:"
msgctxt "wizard_button:cashbook.open_lines,askuser,open_:"
msgid "Open"
msgstr "Öffnen"

View file

@ -8,6 +8,7 @@ from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
from decimal import Decimal
class BookTestCase(ModuleTestCase):
@ -20,13 +21,14 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.btype.rec_name, 'CAS - Cash')
@ -39,18 +41,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -67,18 +72,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -97,18 +105,21 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -125,13 +136,14 @@ class BookTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.state, 'open')
@ -188,18 +200,22 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
usr_lst = ResUser.create([{
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -209,6 +225,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),
self.assertEqual(book.owner.rec_name, 'Frida'),
@ -245,9 +263,9 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_reviewer, = ResGroup.create([{
'name': 'Cashbook Reviewer',
@ -257,10 +275,14 @@ class BookTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_reviewer.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -272,6 +294,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'reviewer': grp_reviewer.id,
'company': company.id,
'currency': company.currency.id,
'btype': types.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@ -301,9 +325,9 @@ class BookTestCase(ModuleTestCase):
ResUser = pool.get('res.user')
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_observer, = ResGroup.create([{
'name': 'Cashbook Observer',
@ -313,10 +337,14 @@ class BookTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_observer.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -328,6 +356,8 @@ class BookTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'observer': grp_observer.id,
'company': company.id,
'currency': company.currency.id,
'btype': types.id,
}])
self.assertEqual(book.rec_name, 'Fridas book'),

View file

@ -7,7 +7,6 @@ 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):
@ -21,12 +20,7 @@ class CategoryTestCase(ModuleTestCase):
Company = pool.get('company.company')
Category = pool.get('cashbook.category')
company = Company.search([])
if len(company) > 0:
company = company[0]
else :
company = create_company(name='m-ds')
company = self.prep_company()
category, = Category.create([{
'company': company.id,
'name': name,
@ -40,7 +34,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
company = create_company(name='m-ds')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@ -73,7 +67,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
company = create_company(name='m-ds')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@ -100,7 +94,7 @@ class CategoryTestCase(ModuleTestCase):
pool = Pool()
Category = pool.get('cashbook.category')
company = create_company(name='m-ds')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@ -126,7 +120,7 @@ class CategoryTestCase(ModuleTestCase):
Account = pool.get('account.account')
Category = pool.get('cashbook.category')
company = create_company(name='m-ds')
company = self.prep_company()
with Transaction().set_context({
'company': company.id,

View file

@ -7,6 +7,7 @@ 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
from datetime import date
@ -14,6 +15,18 @@ class ConfigTestCase(ModuleTestCase):
'Test config type module'
module = 'cashbook'
def prep_company(self):
""" get/create company
"""
Company = Pool().get('company.company')
company = Company.search([])
if len(company) > 0:
company = company[0]
else :
company = create_company(name='m-ds')
return company
@with_transaction()
def test_config_create(self):
""" create config

View file

@ -9,6 +9,7 @@ from trytond.transaction import Transaction
from trytond.exceptions import UserError
from datetime import date
from unittest.mock import MagicMock
from decimal import Decimal
class LineTestCase(ModuleTestCase):
@ -21,23 +22,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -59,29 +65,62 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(Lines.search_count([('cashbook.state', '=', 'open')]), 2)
self.assertEqual(Lines.search_count([('cashbook.state', '=', 'closed')]), 0)
# sorting: date -> state -> id
self.assertEqual(len(book.lines), 2)
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
self.assertEqual(book.lines[0].state, 'edit')
self.assertEqual(book.lines[1].rec_name, '05/02/2022 Text 2')
self.assertEqual(book.lines[1].state, 'edit')
# set to same date
Lines.write(*[
list(book.lines),
{
'date': date(2022, 5, 1),
}])
# check again
book, = Book.search([])
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1')
self.assertEqual(book.lines[0].state, 'edit')
self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 2')
self.assertEqual(book.lines[1].state, 'edit')
# set to 'check', will sort first
Lines.wfcheck([book.lines[1]])
book, = Book.search([])
self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 2')
self.assertEqual(book.lines[0].state, 'check')
self.assertEqual(book.lines[1].rec_name, '05/01/2022 Text 1')
self.assertEqual(book.lines[1].state, 'edit')
@with_transaction()
def test_line_create_check_deny_write(self):
""" create cashbook + line, 'close' book, write to line
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -107,26 +146,31 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Line = pool.get('cashbook.line')
IrDate = pool.get('ir.date')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
IrDate.today = MagicMock(return_value=date(2022, 6, 1))
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -159,21 +203,125 @@ class LineTestCase(ModuleTestCase):
IrDate.today = MagicMock(return_value=date.today())
@with_transaction()
def test_line_create_check_debit_credit(self):
""" create cashbook + line, check calculation of debit/credit
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
Configuration = pool.get('cashbook.configuration')
Category = pool.get('cashbook.category')
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
category2, = Category.create([{
'company': company.id,
'name': 'Category',
}])
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Revenue',
'category': category2.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Expense',
'category': category2.id,
'bookingtype': 'out',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Transfer from',
'category': category2.id,
'bookingtype': 'mvin',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Transfer to',
'category': category2.id,
'bookingtype': 'mvout',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.state, 'open')
self.assertEqual(len(book.lines), 4)
self.assertEqual(book.lines[0].amount, Decimal('1.0'))
self.assertEqual(book.lines[0].bookingtype, 'in')
self.assertEqual(book.lines[0].credit, Decimal('1.0'))
self.assertEqual(book.lines[0].debit, Decimal('0.0'))
self.assertEqual(book.lines[1].amount, Decimal('1.0'))
self.assertEqual(book.lines[1].bookingtype, 'out')
self.assertEqual(book.lines[1].credit, Decimal('0.0'))
self.assertEqual(book.lines[1].debit, Decimal('1.0'))
self.assertEqual(book.lines[2].amount, Decimal('1.0'))
self.assertEqual(book.lines[2].bookingtype, 'mvin')
self.assertEqual(book.lines[2].credit, Decimal('1.0'))
self.assertEqual(book.lines[2].debit, Decimal('0.0'))
self.assertEqual(book.lines[3].amount, Decimal('1.0'))
self.assertEqual(book.lines[3].bookingtype, 'mvout')
self.assertEqual(book.lines[3].credit, Decimal('0.0'))
self.assertEqual(book.lines[3].debit, Decimal('1.0'))
Line.write(*[
[book.lines[0]],
{
'amount': Decimal('2.0'),
}])
self.assertEqual(book.lines[0].amount, Decimal('2.0'))
self.assertEqual(book.lines[0].bookingtype, 'in')
self.assertEqual(book.lines[0].credit, Decimal('2.0'))
self.assertEqual(book.lines[0].debit, Decimal('0.0'))
Line.write(*[
[book.lines[0]],
{
'bookingtype': 'out',
}])
self.assertEqual(book.lines[0].amount, Decimal('2.0'))
self.assertEqual(book.lines[0].bookingtype, 'out')
self.assertEqual(book.lines[0].credit, Decimal('0.0'))
self.assertEqual(book.lines[0].debit, Decimal('2.0'))
Line.write(*[
[book.lines[0]],
{
'bookingtype': 'mvin',
'amount': Decimal('3.0'),
}])
self.assertEqual(book.lines[0].amount, Decimal('3.0'))
self.assertEqual(book.lines[0].bookingtype, 'mvin')
self.assertEqual(book.lines[0].credit, Decimal('3.0'))
self.assertEqual(book.lines[0].debit, Decimal('0.0'))
@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')])
types = self.prep_type()
category = self.prep_category()
company = category.company
company = self.prep_company()
with Transaction().set_context({
'company': company.id,
@ -208,14 +356,20 @@ class LineTestCase(ModuleTestCase):
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category2.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 6, 1),
'description': 'Text 2',
'category': category2.childs[0].id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -257,23 +411,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -288,23 +447,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -324,23 +488,28 @@ class LineTestCase(ModuleTestCase):
"""
pool = Pool()
Book = pool.get('cashbook.book')
Types = pool.get('cashbook.type')
Lines = pool.get('cashbook.line')
types, = Types.search([('short', '=','CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Text 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}, {
'date': date(2022, 5, 2),
'description': 'Text 2',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.name, 'Book 1')
@ -365,19 +534,23 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
usr_lst = ResUser.create([{
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -387,10 +560,14 @@ class LineTestCase(ModuleTestCase):
'name': 'Fridas book',
'owner': usr_lst[0].id,
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@ -427,10 +604,10 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_reviewer, = ResGroup.create([{
'name': 'Cashbook Reviewer',
@ -440,10 +617,14 @@ class LineTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_reviewer.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -456,10 +637,14 @@ class LineTestCase(ModuleTestCase):
'owner': usr_lst[0].id,
'reviewer': grp_reviewer.id,
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),
@ -503,10 +688,10 @@ class LineTestCase(ModuleTestCase):
ResGroup = pool.get('res.group')
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
Types = pool.get('cashbook.type')
types, = Types.search([('short', '=', 'CAS')])
types = self.prep_type()
category = self.prep_category()
company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_observer, = ResGroup.create([{
'name': 'Cashbook Observer',
@ -516,10 +701,14 @@ class LineTestCase(ModuleTestCase):
'login': 'frida',
'name': 'Frida',
'groups': [('add', [grp_cashbook.id])],
'companies': [('add', [company.id])],
'company': company.id,
}, {
'login': 'diego',
'name': 'Diego',
'groups': [('add', [grp_cashbook.id, grp_observer.id])],
'companies': [('add', [company.id])],
'company': company.id,
}])
self.assertEqual(len(usr_lst), 2)
self.assertEqual(usr_lst[0].name, 'Frida')
@ -532,10 +721,14 @@ class LineTestCase(ModuleTestCase):
'owner': usr_lst[0].id,
'observer': grp_observer.id,
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'lines': [('create', [{
'date': date(2022, 5, 1),
'description': 'Test 1',
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('1.0'),
}])],
}])
self.assertEqual(book.rec_name, 'Fridas book'),

View file

@ -13,17 +13,20 @@ class TypeTestCase(ModuleTestCase):
'Test cashbook type module'
module = 'cashbook'
@with_transaction()
def test_type_read_existing(self):
""" read predefined types
def prep_type(self, name='Cash', short='CAS'):
""" create book-type
"""
AccType = Pool().get('cashbook.type')
t_lst = AccType.search([], order=[('name', 'ASC')])
self.assertEqual(len(t_lst), 3)
self.assertEqual(t_lst[0].rec_name, 'CAS - Cash')
self.assertEqual(t_lst[1].rec_name, 'FTD - Fixed-term deposit')
self.assertEqual(t_lst[2].rec_name, 'GIR - Giro')
company = self.prep_company()
at, = AccType.create([{
'name': name,
'short': short,
'company': company.id,
}])
self.assertEqual(at.name, name)
self.assertEqual(at.short, short)
return at
@with_transaction()
def test_type_create(self):
@ -31,9 +34,12 @@ class TypeTestCase(ModuleTestCase):
"""
AccType = Pool().get('cashbook.type')
company = self.prep_company()
at, = AccType.create([{
'name': 'Test 1',
'short': 'T1',
'company': company.id,
}])
self.assertEqual(at.name, 'Test 1')
self.assertEqual(at.short, 'T1')
@ -45,6 +51,7 @@ class TypeTestCase(ModuleTestCase):
[{
'name': 'Test 2',
'short': 'T1',
'company': company.id,
}])
# end TypeTestCase

View file

@ -3,6 +3,7 @@ version=6.0.0
depends:
res
account
currency
xml:
icon.xml
group.xml

View file

@ -4,6 +4,7 @@
# full copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, fields, Unique
from trytond.transaction import Transaction
class Type(ModelSQL, ModelView):
@ -12,6 +13,8 @@ class Type(ModelSQL, ModelView):
name = fields.Char(string='Name', required=True, translate=True)
short = fields.Char(string='Abbreviation', required=True, size=3)
company = fields.Many2One(string='Company', model_name='company.company',
required=True, ondelete="RESTRICT")
@classmethod
def __setup__(cls):
@ -39,4 +42,8 @@ class Type(ModelSQL, ModelView):
('short',) + tuple(clause[1:]),
]
@staticmethod
def default_company():
return Transaction().context.get('company') or None
# end Type

View file

@ -63,18 +63,17 @@ full copyright notices and license terms. -->
<field name="perm_delete" eval="False"/>
</record>
<!-- defaults -->
<record model="cashbook.type" id="atype_cash">
<field name="short">CAS</field>
<field name="name">Cash</field>
<record model="ir.rule.group" id="rg_type_companies">
<field name="name">User in companies</field>
<field name="model"
search="[('model', '=', 'cashbook.type')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="cashbook.type" id="atype_giro">
<field name="short">GIR</field>
<field name="name">Giro</field>
</record>
<record model="cashbook.type" id="atype_fixtermdep">
<field name="short">FTD</field>
<field name="name">Fixed-term deposit</field>
<record model="ir.rule" id="r_type_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rg_type_companies"/>
</record>
</data>

View file

@ -8,9 +8,10 @@ full copyright notices and license terms. -->
<label name="btype"/>
<field name="btype"/>
<label name="currency"/>
<field name="currency"/>
<label name="account"/>
<field name="account"/>
<newline/>
<label name="state"/>
<field name="state"/>

View file

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

View file

@ -7,8 +7,7 @@ full copyright notices and license terms. -->
<separator name="state" colspan="2" string="State"/>
<field name="cashbook" colspan="2"/>
<label name="date"/>
<field name="date"/>
<label id="lab1" string=" " colspan="2"/>
<field name="state"/>
<group id="grpstate" col="2">
@ -16,10 +15,17 @@ full copyright notices and license terms. -->
<button name="wfcheck"/>
</group>
<label name="date"/>
<field name="date"/>
<label name="category"/>
<field name="category"/>
<newline/>
<label name="bookingtype"/>
<field name="bookingtype"/>
<label name="amount"/>
<field name="amount" symbol="currency"/>
<group name="description" colspan="4" col="1" string="Description">
<field name="description"/>
</group>

View file

@ -3,10 +3,13 @@
The COPYRIGHT file at the top level of this repository contains the
full copyright notices and license terms. -->
<tree>
<field name="cashbook"/>
<field name="cashbook" tree_invisible="1"/>
<field name="date"/>
<field name="category_view"/>
<field name="description"/>
<field name="description" expand="1"/>
<field name="credit" sum="Credit"/>
<field name="debit" sum="Debit"/>
<field name="currency"/>
<field name="state"/>
<button name="wfedit"/>
<button name="wfcheck"/>

View file

@ -82,6 +82,10 @@ class OpenCashBook(Wizard):
Configuration = pool.get('cashbook.configuration')
cfg1 = Configuration.get_singleton()
if cfg1 is None:
cfg1 = Configuration()
cfg1.save()
book = getattr(self.askuser, 'cashbook', None)
if book is None:
with Transaction().set_context({