kategorie: sperrt 'Typ' wenn parent existiert, korrekte Anpassung

der sub-kategorien bei Änderung + test
Line: Kategoriefeld prüft Inhalt + tests
This commit is contained in:
Frederik Jaeckel 2022-08-11 11:06:28 +02:00
parent 52ffa0536e
commit 8fd6e0d339
7 changed files with 137 additions and 46 deletions

View file

@ -6,6 +6,7 @@
from trytond.model import ModelView, ModelSQL, fields, Unique, tree, sequence_ordered from trytond.model import ModelView, ModelSQL, fields, Unique, tree, sequence_ordered
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.pool import Pool from trytond.pool import Pool
from trytond.pyson import Eval, If, Bool
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.i18n import gettext from trytond.i18n import gettext
@ -23,7 +24,15 @@ class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView):
name = fields.Char(string='Name', required=True, translate=True) name = fields.Char(string='Name', required=True, translate=True)
description = fields.Char(string='Description', translate=True) description = fields.Char(string='Description', translate=True)
cattype = fields.Selection(string='Type', required=True, cattype = fields.Selection(string='Type', required=True,
help='Type of Category', selection=sel_categorytype) help='Type of Category', selection=sel_categorytype,
states={'readonly': Bool(Eval('parent_cattype'))},
domain=[If(Bool(Eval('parent_cattype')),
('cattype', '=', Eval('parent_cattype', '')),
())],
depends=['parent_cattype'])
parent_cattype = fields.Function(fields.Char(string='Parent Category Type',
readonly=True, states={'invisible': True}),
'on_change_with_parent_cattype')
account = fields.Many2One(string='Account', select=True, account = fields.Many2One(string='Account', select=True,
model_name='account.account', ondelete='RESTRICT') model_name='account.account', ondelete='RESTRICT')
@ -98,6 +107,13 @@ class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView):
('account.code',) + tuple(clause[1:]), ('account.code',) + tuple(clause[1:]),
] ]
@fields.depends('parent', '_parent_parent.cattype')
def on_change_with_parent_cattype(self, name=None):
""" get type of parent category or None
"""
if self.parent:
return self.parent.cattype
@fields.depends('account') @fields.depends('account')
def on_change_with_account_code(self, name=None): def on_change_with_account_code(self, name=None):
""" get code of account """ get code of account
@ -112,33 +128,55 @@ class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView):
return [('account.code',) + tuple(clause[1:])] return [('account.code',) + tuple(clause[1:])]
@classmethod @classmethod
def write(cls, *args): def check_category_hierarchy(cls, categories):
""" parent.cattape == cattype, """ check if current category-type is equal to parent
update sub-categories
""" """
to_write = []
actions = iter(args)
for categories, values in zip(actions, actions):
if 'cattype' in values.keys():
for category in categories: for category in categories:
if category.parent: if category.parent:
if category.parent.cattype != values['cattype']: if category.parent.cattype != category.cattype:
raise UserError(gettext( raise UserError(gettext(
'cashbook.msg_category_type_not_like_parent', 'cashbook.msg_category_type_not_like_parent',
parentname = category.parent.rec_name, parentname = category.parent.rec_name,
catname = category.rec_name, catname = category.rec_name,
)) ))
cats = Category.search([('parent', 'child_of', [x.id for x in categories])])
@classmethod
def create(cls, vlist):
""" add debit/credit
"""
records = super(Category, cls).create(vlist)
cls.check_category_hierarchy(records)
return records
@classmethod
def write(cls, *args):
""" parent.cattape == cattype,
update sub-categories
"""
actions = iter(args)
to_check = []
to_write = []
to_write2 = []
for categories, values in zip(actions, actions):
to_write2.extend([categories, values])
if 'cattype' in values.keys():
# update sub-categories
cats = Category.search([
('parent', 'child_of', [x.id for x in categories])
])
if len(cats) > 0: if len(cats) > 0:
to_write.extend([ to_write.extend([
cats, cats,
{ {
'cattype': values['cattype'], 'cattype': values['cattype'],
}]) }])
to_check.extend(categories)
to_check.extend(cats)
super(Category, cls).write(*args) # add category-updates after regulary writes
if len(to_write) > 0: to_write2.extend(to_write)
print('\n## to_write:',to_write) super(Category, cls).write(*to_write2)
Category.write(*to_write) cls.check_category_hierarchy(to_check)
# end Category # end Category

View file

@ -5,7 +5,7 @@
from trytond.model import ModelView, ModelSQL, Workflow, fields, Check from trytond.model import ModelView, ModelSQL, Workflow, fields, Check
from trytond.pool import Pool from trytond.pool import Pool
from trytond.pyson import Eval, If, Or from trytond.pyson import Eval, If, Or, Bool
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.report import Report from trytond.report import Report
from trytond.exceptions import UserError from trytond.exceptions import UserError
@ -53,7 +53,12 @@ class Line(Workflow, ModelSQL, ModelView):
states=STATES, depends=DEPENDS) states=STATES, depends=DEPENDS)
category = fields.Many2One(string='Category', required=True, category = fields.Many2One(string='Category', required=True,
model_name='cashbook.category', ondelete='RESTRICT', model_name='cashbook.category', ondelete='RESTRICT',
states=STATES, depends=DEPENDS+['bookingtype'], states={
'readonly': Or(
STATES['readonly'],
Bool(Eval('bookingtype')) == False,
),
}, depends=DEPENDS+['bookingtype'],
domain=[ domain=[
If( If(
Eval('bookingtype', '').in_(['in', 'mvin']), Eval('bookingtype', '').in_(['in', 'mvin']),

View file

@ -43,7 +43,7 @@ class BookTestCase(ModuleTestCase):
Book = pool.get('cashbook.book') Book = pool.get('cashbook.book')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -74,7 +74,7 @@ class BookTestCase(ModuleTestCase):
Book = pool.get('cashbook.book') Book = pool.get('cashbook.book')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -107,7 +107,7 @@ class BookTestCase(ModuleTestCase):
Book = pool.get('cashbook.book') Book = pool.get('cashbook.book')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',

View file

@ -13,7 +13,7 @@ class CategoryTestCase(ModuleTestCase):
'Test cashbook categoy module' 'Test cashbook categoy module'
module = 'cashbook' module = 'cashbook'
def prep_category(self, name='Cat1'): def prep_category(self, name='Cat1', cattype='out'):
""" create category """ create category
""" """
pool = Pool() pool = Pool()
@ -24,9 +24,57 @@ class CategoryTestCase(ModuleTestCase):
category, = Category.create([{ category, = Category.create([{
'company': company.id, 'company': company.id,
'name': name, 'name': name,
'cattype': cattype,
}]) }])
return category return category
@with_transaction()
def test_category_create_check_category_type(self):
""" create category, update type of category
"""
pool = Pool()
Category = pool.get('cashbook.category')
company = self.prep_company()
category, = Category.create([{
'company': company.id,
'name': 'Level 1',
'cattype': 'in',
'childs': [('create', [{
'company': company.id,
'name': 'Level 2',
'cattype': 'in',
}])],
}])
self.assertEqual(category.rec_name, 'Level 1')
self.assertEqual(category.cattype, 'in')
self.assertEqual(len(category.childs), 1)
self.assertEqual(category.childs[0].rec_name, 'Level 1/Level 2')
self.assertEqual(category.childs[0].cattype, 'in')
self.assertRaisesRegex(UserError,
'The value for field "Type" in "Category" is not valid according to its domain.',
Category.write,
*[
[category.childs[0]],
{
'cattype': 'out',
},
])
Category.write(*[
[category],
{
'cattype': 'out',
}])
self.assertEqual(category.rec_name, 'Level 1')
self.assertEqual(category.cattype, 'out')
self.assertEqual(len(category.childs), 1)
self.assertEqual(category.childs[0].rec_name, 'Level 1/Level 2')
self.assertEqual(category.childs[0].cattype, 'out')
@with_transaction() @with_transaction()
def test_category_create_nodupl_at_root(self): def test_category_create_nodupl_at_root(self):
""" create category, duplicates are allowed at root-level """ create category, duplicates are allowed at root-level

View file

@ -25,7 +25,7 @@ class LineTestCase(ModuleTestCase):
Lines = pool.get('cashbook.line') Lines = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -102,7 +102,7 @@ class LineTestCase(ModuleTestCase):
Line = pool.get('cashbook.line') Line = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -150,7 +150,7 @@ class LineTestCase(ModuleTestCase):
IrDate = pool.get('ir.date') IrDate = pool.get('ir.date')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
IrDate.today = MagicMock(return_value=date(2022, 6, 1)) IrDate.today = MagicMock(return_value=date(2022, 6, 1))
@ -214,14 +214,10 @@ class LineTestCase(ModuleTestCase):
Category = pool.get('cashbook.category') Category = pool.get('cashbook.category')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category_in = self.prep_category(cattype='in')
category_out = self.prep_category(name='Out Category', cattype='out')
company = self.prep_company() company = self.prep_company()
category2, = Category.create([{
'company': company.id,
'name': 'Category',
}])
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
'btype': types.id, 'btype': types.id,
@ -230,25 +226,25 @@ class LineTestCase(ModuleTestCase):
'lines': [('create', [{ 'lines': [('create', [{
'date': date(2022, 5, 1), 'date': date(2022, 5, 1),
'description': 'Revenue', 'description': 'Revenue',
'category': category2.id, 'category': category_in.id,
'bookingtype': 'in', 'bookingtype': 'in',
'amount': Decimal('1.0'), 'amount': Decimal('1.0'),
}, { }, {
'date': date(2022, 6, 1), 'date': date(2022, 6, 1),
'description': 'Expense', 'description': 'Expense',
'category': category2.id, 'category': category_out.id,
'bookingtype': 'out', 'bookingtype': 'out',
'amount': Decimal('1.0'), 'amount': Decimal('1.0'),
}, { }, {
'date': date(2022, 6, 1), 'date': date(2022, 6, 1),
'description': 'Transfer from', 'description': 'Transfer from',
'category': category2.id, 'category': category_in.id,
'bookingtype': 'mvin', 'bookingtype': 'mvin',
'amount': Decimal('1.0'), 'amount': Decimal('1.0'),
}, { }, {
'date': date(2022, 6, 1), 'date': date(2022, 6, 1),
'description': 'Transfer to', 'description': 'Transfer to',
'category': category2.id, 'category': category_out.id,
'bookingtype': 'mvout', 'bookingtype': 'mvout',
'amount': Decimal('1.0'), 'amount': Decimal('1.0'),
}])], }])],
@ -291,6 +287,7 @@ class LineTestCase(ModuleTestCase):
[book.lines[0]], [book.lines[0]],
{ {
'bookingtype': 'out', 'bookingtype': 'out',
'category': category_out.id,
}]) }])
self.assertEqual(book.lines[0].amount, Decimal('2.0')) self.assertEqual(book.lines[0].amount, Decimal('2.0'))
self.assertEqual(book.lines[0].bookingtype, 'out') self.assertEqual(book.lines[0].bookingtype, 'out')
@ -300,6 +297,7 @@ class LineTestCase(ModuleTestCase):
Line.write(*[ Line.write(*[
[book.lines[0]], [book.lines[0]],
{ {
'category': category_in.id,
'bookingtype': 'mvin', 'bookingtype': 'mvin',
'amount': Decimal('3.0'), 'amount': Decimal('3.0'),
}]) }])
@ -320,7 +318,7 @@ class LineTestCase(ModuleTestCase):
Account = pool.get('account.account') Account = pool.get('account.account')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
with Transaction().set_context({ with Transaction().set_context({
@ -340,10 +338,12 @@ class LineTestCase(ModuleTestCase):
'company': company.id, 'company': company.id,
'name': 'Level1', 'name': 'Level1',
'account': accounts[0].id, 'account': accounts[0].id,
'cattype': 'in',
'childs': [('create', [{ 'childs': [('create', [{
'company': company.id, 'company': company.id,
'name': 'Level2', 'name': 'Level2',
'account': accounts[1].id, 'account': accounts[1].id,
'cattype': 'in',
}])], }])],
}]) }])
self.assertEqual(category2.rec_name, 'Level1 [0123]') self.assertEqual(category2.rec_name, 'Level1 [0123]')
@ -414,7 +414,7 @@ class LineTestCase(ModuleTestCase):
Lines = pool.get('cashbook.line') Lines = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -450,7 +450,7 @@ class LineTestCase(ModuleTestCase):
Lines = pool.get('cashbook.line') Lines = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -491,7 +491,7 @@ class LineTestCase(ModuleTestCase):
Lines = pool.get('cashbook.line') Lines = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
book, = Book.create([{ book, = Book.create([{
'name': 'Book 1', 'name': 'Book 1',
@ -536,7 +536,7 @@ class LineTestCase(ModuleTestCase):
Line = pool.get('cashbook.line') Line = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
usr_lst = ResUser.create([{ usr_lst = ResUser.create([{
@ -606,7 +606,7 @@ class LineTestCase(ModuleTestCase):
Line = pool.get('cashbook.line') Line = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_reviewer, = ResGroup.create([{ grp_reviewer, = ResGroup.create([{
@ -690,7 +690,7 @@ class LineTestCase(ModuleTestCase):
Line = pool.get('cashbook.line') Line = pool.get('cashbook.line')
types = self.prep_type() types = self.prep_type()
category = self.prep_category() category = self.prep_category(cattype='in')
company = self.prep_company() company = self.prep_company()
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
grp_observer, = ResGroup.create([{ grp_observer, = ResGroup.create([{

View file

@ -23,7 +23,7 @@ full copyright notices and license terms. -->
<field name="parent"/> <field name="parent"/>
<label name="sequence"/> <label name="sequence"/>
<field name="sequence"/> <field name="sequence"/>
<field name="childs" colspan="4"/> <field name="childs" colspan="6"/>
</page> </page>
</notebook> </notebook>

View file

@ -17,12 +17,12 @@ full copyright notices and license terms. -->
<label name="date"/> <label name="date"/>
<field name="date"/> <field name="date"/>
<label name="category"/>
<field name="category"/>
<newline/>
<label name="bookingtype"/> <label name="bookingtype"/>
<field name="bookingtype"/> <field name="bookingtype"/>
<newline/>
<label name="category"/>
<field name="category"/>
<label name="amount"/> <label name="amount"/>
<field name="amount" symbol="currency"/> <field name="amount" symbol="currency"/>