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:
parent
52ffa0536e
commit
8fd6e0d339
7 changed files with 137 additions and 46 deletions
68
category.py
68
category.py
|
@ -6,6 +6,7 @@
|
|||
from trytond.model import ModelView, ModelSQL, fields, Unique, tree, sequence_ordered
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If, Bool
|
||||
from trytond.exceptions import UserError
|
||||
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)
|
||||
description = fields.Char(string='Description', translate=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,
|
||||
model_name='account.account', ondelete='RESTRICT')
|
||||
|
@ -98,6 +107,13 @@ class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView):
|
|||
('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')
|
||||
def on_change_with_account_code(self, name=None):
|
||||
""" get code of account
|
||||
|
@ -111,34 +127,56 @@ class Category(tree(separator='/'), sequence_ordered(), ModelSQL, ModelView):
|
|||
"""
|
||||
return [('account.code',) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def check_category_hierarchy(cls, categories):
|
||||
""" check if current category-type is equal to parent
|
||||
"""
|
||||
for category in categories:
|
||||
if category.parent:
|
||||
if category.parent.cattype != category.cattype:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_category_type_not_like_parent',
|
||||
parentname = category.parent.rec_name,
|
||||
catname = category.rec_name,
|
||||
))
|
||||
|
||||
@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
|
||||
"""
|
||||
to_write = []
|
||||
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():
|
||||
for category in categories:
|
||||
if category.parent:
|
||||
if category.parent.cattype != values['cattype']:
|
||||
raise UserError(gettext(
|
||||
'cashbook.msg_category_type_not_like_parent',
|
||||
parentname = category.parent.rec_name,
|
||||
catname = category.rec_name,
|
||||
))
|
||||
cats = Category.search([('parent', 'child_of', [x.id for x in categories])])
|
||||
# update sub-categories
|
||||
cats = Category.search([
|
||||
('parent', 'child_of', [x.id for x in categories])
|
||||
])
|
||||
if len(cats) > 0:
|
||||
to_write.extend([
|
||||
cats,
|
||||
{
|
||||
'cattype': values['cattype'],
|
||||
}])
|
||||
to_check.extend(categories)
|
||||
to_check.extend(cats)
|
||||
|
||||
super(Category, cls).write(*args)
|
||||
if len(to_write) > 0:
|
||||
print('\n## to_write:',to_write)
|
||||
Category.write(*to_write)
|
||||
# add category-updates after regulary writes
|
||||
to_write2.extend(to_write)
|
||||
super(Category, cls).write(*to_write2)
|
||||
cls.check_category_hierarchy(to_check)
|
||||
|
||||
# end Category
|
||||
|
|
9
line.py
9
line.py
|
@ -5,7 +5,7 @@
|
|||
|
||||
from trytond.model import ModelView, ModelSQL, Workflow, fields, Check
|
||||
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.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
|
@ -53,7 +53,12 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
states=STATES, depends=DEPENDS)
|
||||
category = fields.Many2One(string='Category', required=True,
|
||||
model_name='cashbook.category', ondelete='RESTRICT',
|
||||
states=STATES, depends=DEPENDS+['bookingtype'],
|
||||
states={
|
||||
'readonly': Or(
|
||||
STATES['readonly'],
|
||||
Bool(Eval('bookingtype')) == False,
|
||||
),
|
||||
}, depends=DEPENDS+['bookingtype'],
|
||||
domain=[
|
||||
If(
|
||||
Eval('bookingtype', '').in_(['in', 'mvin']),
|
||||
|
|
|
@ -43,7 +43,7 @@ class BookTestCase(ModuleTestCase):
|
|||
Book = pool.get('cashbook.book')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -74,7 +74,7 @@ class BookTestCase(ModuleTestCase):
|
|||
Book = pool.get('cashbook.book')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -107,7 +107,7 @@ class BookTestCase(ModuleTestCase):
|
|||
Book = pool.get('cashbook.book')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
|
|
@ -13,7 +13,7 @@ class CategoryTestCase(ModuleTestCase):
|
|||
'Test cashbook categoy module'
|
||||
module = 'cashbook'
|
||||
|
||||
def prep_category(self, name='Cat1'):
|
||||
def prep_category(self, name='Cat1', cattype='out'):
|
||||
""" create category
|
||||
"""
|
||||
pool = Pool()
|
||||
|
@ -24,9 +24,57 @@ class CategoryTestCase(ModuleTestCase):
|
|||
category, = Category.create([{
|
||||
'company': company.id,
|
||||
'name': name,
|
||||
'cattype': cattype,
|
||||
}])
|
||||
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()
|
||||
def test_category_create_nodupl_at_root(self):
|
||||
""" create category, duplicates are allowed at root-level
|
||||
|
|
|
@ -25,7 +25,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Lines = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -102,7 +102,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Line = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -150,7 +150,7 @@ class LineTestCase(ModuleTestCase):
|
|||
IrDate = pool.get('ir.date')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
IrDate.today = MagicMock(return_value=date(2022, 6, 1))
|
||||
|
||||
|
@ -214,14 +214,10 @@ class LineTestCase(ModuleTestCase):
|
|||
Category = pool.get('cashbook.category')
|
||||
|
||||
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()
|
||||
|
||||
category2, = Category.create([{
|
||||
'company': company.id,
|
||||
'name': 'Category',
|
||||
}])
|
||||
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
'btype': types.id,
|
||||
|
@ -230,25 +226,25 @@ class LineTestCase(ModuleTestCase):
|
|||
'lines': [('create', [{
|
||||
'date': date(2022, 5, 1),
|
||||
'description': 'Revenue',
|
||||
'category': category2.id,
|
||||
'category': category_in.id,
|
||||
'bookingtype': 'in',
|
||||
'amount': Decimal('1.0'),
|
||||
}, {
|
||||
'date': date(2022, 6, 1),
|
||||
'description': 'Expense',
|
||||
'category': category2.id,
|
||||
'category': category_out.id,
|
||||
'bookingtype': 'out',
|
||||
'amount': Decimal('1.0'),
|
||||
}, {
|
||||
'date': date(2022, 6, 1),
|
||||
'description': 'Transfer from',
|
||||
'category': category2.id,
|
||||
'category': category_in.id,
|
||||
'bookingtype': 'mvin',
|
||||
'amount': Decimal('1.0'),
|
||||
}, {
|
||||
'date': date(2022, 6, 1),
|
||||
'description': 'Transfer to',
|
||||
'category': category2.id,
|
||||
'category': category_out.id,
|
||||
'bookingtype': 'mvout',
|
||||
'amount': Decimal('1.0'),
|
||||
}])],
|
||||
|
@ -291,6 +287,7 @@ class LineTestCase(ModuleTestCase):
|
|||
[book.lines[0]],
|
||||
{
|
||||
'bookingtype': 'out',
|
||||
'category': category_out.id,
|
||||
}])
|
||||
self.assertEqual(book.lines[0].amount, Decimal('2.0'))
|
||||
self.assertEqual(book.lines[0].bookingtype, 'out')
|
||||
|
@ -300,6 +297,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Line.write(*[
|
||||
[book.lines[0]],
|
||||
{
|
||||
'category': category_in.id,
|
||||
'bookingtype': 'mvin',
|
||||
'amount': Decimal('3.0'),
|
||||
}])
|
||||
|
@ -320,7 +318,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Account = pool.get('account.account')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -340,10 +338,12 @@ class LineTestCase(ModuleTestCase):
|
|||
'company': company.id,
|
||||
'name': 'Level1',
|
||||
'account': accounts[0].id,
|
||||
'cattype': 'in',
|
||||
'childs': [('create', [{
|
||||
'company': company.id,
|
||||
'name': 'Level2',
|
||||
'account': accounts[1].id,
|
||||
'cattype': 'in',
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(category2.rec_name, 'Level1 [0123]')
|
||||
|
@ -414,7 +414,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Lines = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -450,7 +450,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Lines = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -491,7 +491,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Lines = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
|
@ -536,7 +536,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Line = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
|
||||
usr_lst = ResUser.create([{
|
||||
|
@ -606,7 +606,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Line = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
|
||||
grp_reviewer, = ResGroup.create([{
|
||||
|
@ -690,7 +690,7 @@ class LineTestCase(ModuleTestCase):
|
|||
Line = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category()
|
||||
category = self.prep_category(cattype='in')
|
||||
company = self.prep_company()
|
||||
grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')])
|
||||
grp_observer, = ResGroup.create([{
|
||||
|
|
|
@ -23,7 +23,7 @@ full copyright notices and license terms. -->
|
|||
<field name="parent"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<field name="childs" colspan="4"/>
|
||||
<field name="childs" colspan="6"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@ full copyright notices and license terms. -->
|
|||
|
||||
<label name="date"/>
|
||||
<field name="date"/>
|
||||
<label name="category"/>
|
||||
<field name="category"/>
|
||||
<newline/>
|
||||
|
||||
<label name="bookingtype"/>
|
||||
<field name="bookingtype"/>
|
||||
<newline/>
|
||||
|
||||
<label name="category"/>
|
||||
<field name="category"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount" symbol="currency"/>
|
||||
|
||||
|
|
Loading…
Reference in a new issue