diff --git a/category.py b/category.py
index c4d9ac8..661c780 100644
--- a/category.py
+++ b/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
diff --git a/line.py b/line.py
index 40d6f6f..ee559c7 100644
--- a/line.py
+++ b/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']),
diff --git a/tests/test_book.py b/tests/test_book.py
index 0db06ec..29a90f3 100644
--- a/tests/test_book.py
+++ b/tests/test_book.py
@@ -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',
diff --git a/tests/test_category.py b/tests/test_category.py
index f4a7325..39759a1 100644
--- a/tests/test_category.py
+++ b/tests/test_category.py
@@ -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
diff --git a/tests/test_line.py b/tests/test_line.py
index 5f7d905..63c644f 100644
--- a/tests/test_line.py
+++ b/tests/test_line.py
@@ -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([{
diff --git a/view/category_form.xml b/view/category_form.xml
index baa4999..c06201b 100644
--- a/view/category_form.xml
+++ b/view/category_form.xml
@@ -23,7 +23,7 @@ full copyright notices and license terms. -->
-
+
diff --git a/view/line_form.xml b/view/line_form.xml
index 9dcd58c..45f03c6 100644
--- a/view/line_form.xml
+++ b/view/line_form.xml
@@ -17,12 +17,12 @@ full copyright notices and license terms. -->
-
-
-
-
+
+
+
+