# -*- 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 from unittest.mock import MagicMock from decimal import Decimal class LineTestCase(ModuleTestCase): 'Test cashbook line module' module = 'cashbook' @with_transaction() def test_line_create_check_names_search(self): """ create cashbook + line """ pool = Pool() Book = pool.get('cashbook.book') Lines = pool.get('cashbook.line') 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') self.assertEqual(book.btype.rec_name, 'CAS - Cash') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) self.assertEqual(book.lines[0].date, date(2022, 5, 1)) self.assertEqual(book.lines[0].rec_name, '05/01/2022 Text 1') self.assertEqual(book.lines[0].state_cashbook, 'open') self.assertEqual(book.lines[1].date, date(2022, 5, 2)) self.assertEqual(book.lines[1].rec_name, '05/02/2022 Text 2') self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1')]), 1) self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1a')]), 0) self.assertEqual(Lines.search_count([('rec_name', 'ilike', 'text%')]), 2) self.assertEqual(Lines.search_count([('state_cashbook', '=', 'open')]), 2) self.assertEqual(Lines.search_count([('state_cashbook', '=', 'closed')]), 0) 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') Line = pool.get('cashbook.line') 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') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) Book.wfclosed([book]) self.assertEqual(book.state, 'closed') self.assertRaisesRegex(UserError, "The cash book 'Book 1' is 'Closed' and cannot be changed.", Line.write, *[ [book.lines[0]], { 'description': 'should be denied', }, ]) @with_transaction() def test_line_create_check_month(self): """ create cashbook + line, check 'month' + search """ pool = Pool() Book = pool.get('cashbook.book') Line = pool.get('cashbook.line') IrDate = pool.get('ir.date') 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') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) self.assertEqual(book.lines[0].date, date(2022, 5, 1)) self.assertEqual(book.lines[0].month, 1) self.assertEqual(book.lines[1].date, date(2022, 6, 1)) self.assertEqual(book.lines[1].month, 0) l1, = Line.search([('month', '=', 0)]) self.assertEqual(l1.date, date(2022, 6, 1)) l1, = Line.search([('month', '=', 1)]) self.assertEqual(l1.date, date(2022, 5, 1)) IrDate.today = MagicMock(return_value=date(2022, 6, 30)) l1, = Line.search([('month', '=', 0)]) self.assertEqual(l1.date, date(2022, 6, 1)) l1, = Line.search([('month', '=', 1)]) self.assertEqual(l1.date, date(2022, 5, 1)) self.assertEqual(Line.search_count([('month', '=', 2)]), 0) IrDate.today = MagicMock(return_value=date(2022, 7, 1)) self.assertEqual(Line.search_count([('month', '=', 0)]), 0) l1, = Line.search([('month', '=', 1)]) self.assertEqual(l1.date, date(2022, 6, 1)) 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') Line = pool.get('cashbook.line') Configuration = pool.get('cashbook.configuration') Category = pool.get('cashbook.category') Account = pool.get('account.account') types = self.prep_type() category = self.prep_category() company = self.prep_company() with Transaction().set_context({ 'company': company.id, }): accounts = Account.create([{ 'name': 'Account No 1', 'code': '0123', }, { 'name': 'Account No 2', 'code': '2345', }]) self.assertEqual(accounts[0].rec_name, '0123 - Account No 1') self.assertEqual(accounts[1].rec_name, '2345 - Account No 2') category2, = Category.create([{ 'company': company.id, 'name': 'Level1', 'account': accounts[0].id, 'childs': [('create', [{ 'company': company.id, 'name': 'Level2', 'account': accounts[1].id, }])], }]) self.assertEqual(category2.rec_name, 'Level1 [0123]') self.assertEqual(len(category2.childs), 1) self.assertEqual(category2.childs[0].rec_name, 'Level1/Level2 [2345]') cfg1 = Configuration() cfg1.save() 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') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) self.assertEqual(cfg1.catnamelong, True) self.assertEqual(cfg1.cataccno, True) self.assertEqual(book.lines[0].category.rec_name, 'Level1 [0123]') self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2 [2345]') self.assertEqual(book.lines[0].category_view, 'Level1 [0123]') self.assertEqual(book.lines[1].category_view, 'Level1/Level2 [2345]') cfg1.cataccno = False cfg1.save() self.assertEqual(book.lines[0].category.rec_name, 'Level1') self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2') self.assertEqual(book.lines[0].category_view, 'Level1') self.assertEqual(book.lines[1].category_view, 'Level1/Level2') cfg1.catnamelong = False cfg1.save() self.assertEqual(book.lines[0].category.rec_name, 'Level1') self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2') self.assertEqual(book.lines[0].category_view, 'Level1') self.assertEqual(book.lines[1].category_view, 'Level2') cfg1.cataccno = True cfg1.save() self.assertEqual(book.lines[0].category.rec_name, 'Level1 [0123]') self.assertEqual(book.lines[1].category.rec_name, 'Level1/Level2 [2345]') self.assertEqual(book.lines[0].category_view, 'Level1 [0123]') self.assertEqual(book.lines[1].category_view, 'Level2 [2345]') @with_transaction() def test_line_delete_with_book_in_open_state(self): """ create cashbook + line, book in state=open, delete a line """ pool = Pool() Book = pool.get('cashbook.book') Lines = pool.get('cashbook.line') 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') self.assertEqual(len(book.lines), 2) self.assertEqual(book.state, 'open') Lines.delete([book.lines[0]]) @with_transaction() def test_line_delete_with_book_in_closed_state(self): """ create cashbook + line, book in state=closed, delete a line """ pool = Pool() Book = pool.get('cashbook.book') Lines = pool.get('cashbook.line') 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') self.assertEqual(len(book.lines), 2) self.assertEqual(book.state, 'open') Book.wfclosed([book]) self.assertEqual(book.state, 'closed') self.assertRaisesRegex(UserError, "The cashbook line '05/01/2022 Text 1' cannot be deleted because the Cashbook 'Book 1' is in state 'Closed'.", Lines.delete, [book.lines[0]]) @with_transaction() def test_line_delete_with_line_in_check_state(self): """ create cashbook + line, line in state=check, delete a line """ pool = Pool() Book = pool.get('cashbook.book') Lines = pool.get('cashbook.line') 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') self.assertEqual(len(book.lines), 2) self.assertEqual(book.state, 'open') self.assertEqual(book.lines[0].state, 'edit') Lines.wfcheck([book.lines[0]]) self.assertEqual(book.lines[0].state, 'check') self.assertRaisesRegex(UserError, "The cashbook line '05/01/2022 Text 1' cannot be deleted, its in state 'Checked'.", Lines.delete, [book.lines[0]]) @with_transaction() def test_line_permission_owner(self): """ create book+line + 2x users, add users to group, check access """ pool = Pool() ResUser = pool.get('res.user') ResGroup = pool.get('res.group') Book = pool.get('cashbook.book') Line = pool.get('cashbook.line') 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') self.assertEqual(usr_lst[1].name, 'Diego') book, = Book.create([{ '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'), self.assertEqual(book.owner.rec_name, 'Frida'), with Transaction().set_context({ '_check_access': True, }): # change to user 'diego' , try access with Transaction().set_user(usr_lst[1].id): lines = Line.search([]) self.assertEqual(len(lines), 0) # change to user 'frida' read/write book with Transaction().set_user(usr_lst[0].id): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book') self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1') Line.write(*[ lines, { 'description': 'Test 2', }]) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2') @with_transaction() def test_line_permission_reviewer(self): """ create book+line + 2x users + 1x reviewer-group, add users to group, check access """ pool = Pool() ResUser = pool.get('res.user') ResGroup = pool.get('res.group') Book = pool.get('cashbook.book') Line = pool.get('cashbook.line') 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', }]) 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, grp_reviewer.id])], 'companies': [('add', [company.id])], 'company': company.id, }]) self.assertEqual(len(usr_lst), 2) self.assertEqual(usr_lst[0].name, 'Frida') self.assertEqual(usr_lst[1].name, 'Diego') # create cashbook # add reviewer-group to allow write for users in group book, = Book.create([{ 'name': 'Fridas book', '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'), self.assertEqual(book.owner.rec_name, 'Frida'), with Transaction().set_context({ '_check_access': True, }): # change to user 'diego' , try access with Transaction().set_user(usr_lst[1].id): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0].cashbook.reviewer.users), 1) self.assertEqual(lines[0].cashbook.reviewer.users[0].rec_name, 'Diego') self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1') Line.write(*[ lines, { 'description': 'Test 2', }]) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2') # change to user 'frida' read/write line with Transaction().set_user(usr_lst[0].id): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2') Line.write(*[ lines, { 'description': 'Test 3', }]) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 3') @with_transaction() def test_line_permission_observer(self): """ create book+line + 2x users + 1x observer-group, add users to group, check access """ pool = Pool() ResUser = pool.get('res.user') ResGroup = pool.get('res.group') Book = pool.get('cashbook.book') Line = pool.get('cashbook.line') 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', }]) 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, grp_observer.id])], 'companies': [('add', [company.id])], 'company': company.id, }]) self.assertEqual(len(usr_lst), 2) self.assertEqual(usr_lst[0].name, 'Frida') self.assertEqual(usr_lst[1].name, 'Diego') # create cashbook # add reviewer-group to allow write for users in group book, = Book.create([{ 'name': 'Fridas book', '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'), self.assertEqual(book.owner.rec_name, 'Frida'), with Transaction().set_context({ '_check_access': True, }): # change to user 'diego' , try access with Transaction().set_user(usr_lst[1].id): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(len(lines[0].cashbook.observer.users), 1) self.assertEqual(lines[0].cashbook.observer.users[0].rec_name, 'Diego') self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1') self.assertRaisesRegex(UserError, 'You are not allowed to write to records "[0-9]{1,}" of "Cashbook Line" because of at least one of these rules:\nOwners and reviewers: Cashbook line write - ', Line.write, *[ lines, { 'description': 'Test 2', }, ]) # change to user 'frida' read/write line with Transaction().set_user(usr_lst[0].id): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1') Line.write(*[ lines, { 'description': 'Test 2', }]) self.assertEqual(lines[0].rec_name, '05/01/2022 Test 2') # end LineTestCase