From 5fdbb0ce8971267f38beefcde2a2b7e7ed9e248e Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Tue, 16 Aug 2022 14:16:14 +0200 Subject: [PATCH] =?UTF-8?q?line:=20spalte=20'empf=C3=A4nger',=20Feld=20're?= =?UTF-8?q?ference'=20+=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- line.py | 119 ++++++++++++++++++-- locale/de.po | 56 ++++++++++ locale/en.po | 84 +++++++++++++- message.xml | 16 +++ tests/test_book.py | 8 ++ tests/test_config.py | 10 ++ tests/test_line.py | 209 ++++++++++++++++++++++++++++++++--- tests/test_reconciliation.py | 38 +++++-- view/line_form.xml | 9 ++ view/line_list.xml | 3 +- 10 files changed, 510 insertions(+), 42 deletions(-) diff --git a/line.py b/line.py index 64734a5..5839626 100644 --- a/line.py +++ b/line.py @@ -17,6 +17,11 @@ from sql.conditionals import Case from .book import sel_state_book +sel_payee = [ + ('cashbook.book', 'Cashbook'), + ('party.party', 'Party') + ] + sel_linetype = [ ('edit', 'Edit'), ('check', 'Checked'), @@ -51,13 +56,15 @@ class Line(Workflow, ModelSQL, ModelView): 'on_change_with_month', searcher='search_month') description = fields.Text(string='Description', states=STATES, depends=DEPENDS) - category = fields.Many2One(string='Category', required=True, + category = fields.Many2One(string='Category', model_name='cashbook.category', ondelete='RESTRICT', states={ 'readonly': Or( STATES['readonly'], Bool(Eval('bookingtype')) == False, ), + 'required': Eval('bookingtype', '').in_(['in', 'out']), + 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']), }, depends=DEPENDS+['bookingtype'], domain=[ If( @@ -78,14 +85,18 @@ class Line(Workflow, ModelSQL, ModelView): credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)), required=True, readonly=True, depends=['currency_digits']) - booktransf = fields.Many2One(string='Transfer Cashbook', + # party or cashbook as counterpart + booktransf = fields.Many2One(string='Source/Dest', ondelete='RESTRICT', model_name='cashbook.book', - domain=[('cashbook.owner.id', '=', Eval('context',{}).get('user', -1))], + domain=[ + ('owner.id', '=', Eval('owner_cashbook', -1)), + ('id', '!=', Eval('cashbook', -1)), + ], states={ 'readonly': STATES['readonly'], 'invisible': ~Eval('bookingtype', '').in_(['mvin', 'mvout']), 'required': Eval('bookingtype', '').in_(['mvin', 'mvout']), - }, depends=DEPENDS+['bookingtype']) + }, depends=DEPENDS+['bookingtype', 'owner_cashbook', 'cashbook']) party = fields.Many2One(string='Party', model_name='party.party', ondelete='RESTRICT', states={ @@ -93,6 +104,20 @@ class Line(Workflow, ModelSQL, ModelView): 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']), 'required': Eval('bookingtype', '').in_(['in', 'out']), }, depends=DEPENDS+['bookingtype']) + payee = fields.Function(fields.Reference(string='Payee', readonly=True, + selection=sel_payee), 'on_change_with_payee', searcher='search_payee') + + # link to lines created by this record + reference = fields.Many2One(string='Reference', readonly=True, select=True, + states={ + 'invisible': ~Bool(Eval('reference')), + }, model_name='cashbook.line', ondelete='CASCADE', + help='The current row was created by and is controlled by the reference row.') + references = fields.One2Many(string='References', model_name='cashbook.line', + help='The rows are created and managed by the current record.', + states={ + 'invisible': ~Bool(Eval('references')), + }, field='reference', readonly=True) reconciliation = fields.Many2One(string='Reconciliation', readonly=True, model_name='cashbook.recon', ondelete='SET NULL', @@ -116,6 +141,9 @@ class Line(Workflow, ModelSQL, ModelView): state_cashbook = fields.Function(fields.Selection(string='State of Cashbook', readonly=True, states={'invisible': True}, selection=sel_state_book), 'on_change_with_state_cashbook', searcher='search_state_cashbook') + owner_cashbook = fields.Function(fields.Many2One(string='Owner', readonly=True, + states={'invisible': True}, model_name='res.user'), + 'on_change_with_owner_cashbook') #image = fields.Binary... @@ -138,7 +166,8 @@ class Line(Workflow, ModelSQL, ModelView): cls._buttons.update({ 'wfedit': { 'invisible': Eval('state', '') != 'check', - 'depends': ['state'], + 'readonly': Bool(Eval('reference')), + 'depends': ['state', 'reference'], }, 'wfcheck': { 'invisible': Eval('state') != 'edit', @@ -156,7 +185,27 @@ class Line(Workflow, ModelSQL, ModelView): def wfedit(cls, lines): """ edit line """ - pass + pool = Pool() + Line2 = pool.get('cashbook.line') + + to_delete_line = [] + for line in lines: + if line.reference: + if Transaction().context.get('line.allow.wfedit', False) == False: + raise UserError(gettext( + 'cashbook.msg_line_denywf_by_reference', + recname = line.reference.rec_name, + cbook = line.reference.cashbook.rec_name, + )) + # delete references + to_delete_line.extend(list(line.references)) + + if len(to_delete_line) > 0: + with Transaction().set_context({ + 'line.allow.wfedit': True, + }): + Line2.wfedit(to_delete_line) + Line2.delete(to_delete_line) @classmethod @ModelView.button @@ -166,7 +215,9 @@ class Line(Workflow, ModelSQL, ModelView): """ pool = Pool() Recon = pool.get('cashbook.recon') + Line2 = pool.get('cashbook.line') + to_create_line = [] for line in lines: # deny if date is in range of existing reconciliation # allow cashbook-line at range-limits @@ -194,6 +245,23 @@ class Line(Workflow, ModelSQL, ModelView): 'cashbook.msg_line_err_write_to_reconciled', datetxt = Report.format_date(line.date), )) + # in case of 'mvin' or 'mvout' - create counterpart + if (line.bookingtype in ['mvout', 'mvin']) and (line.reference is None): + to_create_line.append({ + 'cashbook': line.booktransf.id, + 'bookingtype': 'mvin' if line.bookingtype == 'mvout' else 'mvout', + 'date': line.date, + 'description': line.description, + 'amount': line.amount, + 'credit': line.debit, + 'debit': line.credit, + 'booktransf': line.cashbook.id, + 'reference': line.id, + }) + + if len(to_create_line) > 0: + new_lines = Line2.create(to_create_line) + Line2.wfcheck(new_lines) @classmethod @ModelView.button @@ -232,12 +300,17 @@ class Line(Workflow, ModelSQL, ModelView): def get_rec_name(self, name): """ short + name """ - return '%(date)s|%(amount)s %(symbol)s|%(desc)s [%(category)s]' % { + credit = self.credit if self.credit is not None else Decimal('0.0') + debit = self.debit if self.debit is not None else Decimal('0.0') + return '%(date)s|%(type)s|%(amount)s %(symbol)s|%(desc)s [%(category)s]' % { 'date': Report.format_date(self.date), 'desc': (self.description or '-')[:40], - 'amount': Report.format_number(self.amount or 0.0, None), + 'amount': Report.format_number(credit - debit, None), 'symbol': getattr(self.currency, 'symbol', '-'), - 'category': self.category_view, + 'category': self.category_view \ + if self.bookingtype in ['in', 'out'] \ + else getattr(self.booktransf, 'rec_name', '-'), + 'type': gettext('cashbook.msg_line_bookingtype_%s' % self.bookingtype), } @staticmethod @@ -271,6 +344,27 @@ class Line(Workflow, ModelSQL, ModelView): return [tab2] + @fields.depends('party', 'booktransf', 'bookingtype') + def on_change_with_payee(self, name=None): + """ get party or cashbook + """ + if self.bookingtype: + if self.bookingtype in ['in', 'out']: + if self.party: + return 'party.party,%d' % self.party.id + elif self.bookingtype in ['mvin', 'mvout']: + if self.booktransf: + return 'cashbook.book,%d' % self.booktransf.id + + @classmethod + def search_payee(cls, names, clause): + """ search in payee for party or cashbook + """ + return ['OR', + ('party.rec_name',) + tuple(clause[1:]), + ('booktransf.rec_name',) + tuple(clause[1:]), + ] + @fields.depends('category') def on_change_with_category_view(self, name=None): """ show optimizef form of category for list-view @@ -320,6 +414,13 @@ class Line(Workflow, ModelSQL, ModelView): ) return [('id', 'in', query)] + @fields.depends('cashbook', '_parent_cashbook.owner') + def on_change_with_owner_cashbook(self, name=None): + """ get current owner + """ + if self.cashbook: + return self.cashbook.owner.id + @fields.depends('cashbook', '_parent_cashbook.state') def on_change_with_state_cashbook(self, name=None): """ get state of cashbook diff --git a/locale/de.po b/locale/de.po index ea9a08d..b441bad 100644 --- a/locale/de.po +++ b/locale/de.po @@ -102,6 +102,26 @@ msgctxt "model:ir.message,text:msg_recon_predecessor_not_done" msgid "The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'." msgstr "Der Vorgänger '%(recname_p)s' muss sich im Status 'Fertig' befinden, bevor Sie den aktuellen Abgleich '%(recname_c)s' prüfen können." +msgctxt "model:ir.message,text:msg_line_bookingtype_in" +msgid "Rev" +msgstr "Einn." + +msgctxt "model:ir.message,text:msg_line_bookingtype_out" +msgid "Exp" +msgstr "Ausg." + +msgctxt "model:ir.message,text:msg_line_bookingtype_mvin" +msgid "from" +msgstr "von" + +msgctxt "model:ir.message,text:msg_line_bookingtype_mvout" +msgid "to" +msgstr "nach" + +msgctxt "model:ir.message,text:msg_line_denywf_by_reference" +msgid "The current line is managed by the cashbook line '%(recname)s', cashbook: '%(cbook)s'." +msgstr "Die aktuelle Zeile wird durch die Kassenbuchzeile '%(recname)s' verwaltet, Kassenbuch: '%(cbook)s'." + ############# # res.group # @@ -494,6 +514,42 @@ msgctxt "field:cashbook.line,party:" msgid "Party" msgstr "Partei" +msgctxt "field:cashbook.line,booktransf:" +msgid "Source/Dest" +msgstr "Quelle/Ziel" + +msgctxt "field:cashbook.line,owner_cashbook:" +msgid "Owner" +msgstr "Eigentümer" + +msgctxt "field:cashbook.line,reference:" +msgid "Reference" +msgstr "Referenz" + +msgctxt "help:cashbook.line,reference:" +msgid "The current row was created by and is controlled by the reference row." +msgstr "Die aktuelle Zeile wurde durch die Referenzzeile erstellt und wird von dieser gesteuert." + +msgctxt "field:cashbook.line,references:" +msgid "References" +msgstr "Referenzen" + +msgctxt "help:cashbook.line,references:" +msgid "The rows are created and managed by the current record." +msgstr "Die Zeilen werden durch den aktuellen Datensatz erstellt und verwaltet." + +msgctxt "field:cashbook.line,payee:" +msgid "Payee" +msgstr "Empfänger" + +msgctxt "selection:cashbook.line,payee:" +msgid "Cashbook" +msgstr "Kassenbuch" + +msgctxt "selection:cashbook.line,payee:" +msgid "Party" +msgstr "Partei" + ################# # cashbook.type # diff --git a/locale/en.po b/locale/en.po index d8e335b..a73b4e2 100644 --- a/locale/en.po +++ b/locale/en.po @@ -35,8 +35,8 @@ msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linest msgstr "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linestate)s'." msgctxt "model:ir.message,text:msg_line_deny_stateedit_with_recon" -msgid "The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation." -msgstr "The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation." +msgid "The status cannot be changed to 'Edit' while the line '%(recname)s' is associated with a reconciliation." +msgstr "The status cannot be changed to 'Edit' while the line '%(recname)s' is associated with a reconciliation." msgctxt "model:ir.message,text:msg_line_deny_write" msgid "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed." @@ -82,6 +82,42 @@ msgctxt "model:ir.message,text:msg_recon_err_overlap" msgid "The date range overlaps with another reconciliation." msgstr "The date range overlaps with another reconciliation." +msgctxt "model:ir.message,text:msg_line_err_write_to_reconciled" +msgid "For the date '%(datetxt)s' there is already a completed reconciliation. Use a different date." +msgstr "For the date '%(datetxt)s' there is already a completed reconciliation. Use a different date." + +msgctxt "model:ir.message,text:msg_recon_lines_no_linked" +msgid "In the date range from %(date_from)s to %(date_to)s, there are still cashbook lines that do not belong to any reconciliation." +msgstr "In the date range from %(date_from)s to %(date_to)s, there are still cashbook lines that do not belong to any reconciliation." + +msgctxt "model:ir.message,text:msg_recon_date_from_to_mismatch" +msgid "The start date '%(datefrom)s' of the current reconciliation '%(recname)s' must correspond to the end date '%(dateto)s' of the predecessor." +msgstr "The start date '%(datefrom)s' of the current reconciliation '%(recname)s' must correspond to the end date '%(dateto)s' of the predecessor." + +msgctxt "model:ir.message,text:msg_recon_predecessor_not_done" +msgid "The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'." +msgstr "The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'." + +msgctxt "model:ir.message,text:msg_line_bookingtype_in" +msgid "Rev" +msgstr "Rev" + +msgctxt "model:ir.message,text:msg_line_bookingtype_out" +msgid "Exp" +msgstr "Exp" + +msgctxt "model:ir.message,text:msg_line_bookingtype_mvin" +msgid "from" +msgstr "from" + +msgctxt "model:ir.message,text:msg_line_bookingtype_mvout" +msgid "to" +msgstr "to" + +msgctxt "model:ir.message,text:msg_line_denywf_by_reference" +msgid "The current line is managed by the cashbook line '%(recname)s', cashbook: '%(cbook)s'." +msgstr "The current line is managed by the cashbook line '%(recname)s', cashbook: '%(cbook)s'." + msgctxt "model:res.group,name:group_cashbook" msgid "Cashbook" msgstr "Cashbook" @@ -438,6 +474,46 @@ msgctxt "field:cashbook.line,reconciliation:" msgid "Reconciliation" msgstr "Reconciliation" +msgctxt "field:cashbook.line,party:" +msgid "Party" +msgstr "Party" + +msgctxt "field:cashbook.line,booktransf:" +msgid "Source/Dest" +msgstr "Source/Dest" + +msgctxt "field:cashbook.line,owner_cashbook:" +msgid "Owner" +msgstr "Owner" + +msgctxt "field:cashbook.line,reference:" +msgid "Reference" +msgstr "Reference" + +msgctxt "help:cashbook.line,reference:" +msgid "The current row was created by and is controlled by the reference row." +msgstr "The current row was created by and is controlled by the reference row." + +msgctxt "field:cashbook.line,references:" +msgid "References" +msgstr "References" + +msgctxt "help:cashbook.line,references:" +msgid "The rows are created and managed by the current record." +msgstr "The rows are created and managed by the current record." + +msgctxt "field:cashbook.line,payee:" +msgid "Payee" +msgstr "Payee" + +msgctxt "selection:cashbook.line,payee:" +msgid "Cashbook" +msgstr "Cashbook" + +msgctxt "selection:cashbook.line,payee:" +msgid "Party" +msgstr "Party" + msgctxt "model:cashbook.type,name:" msgid "Cashbook Type" msgstr "Cashbook Type" @@ -738,3 +814,7 @@ msgctxt "field:cashbook.recon,start_amount:" msgid "Start Amount" msgstr "Start Amount" +msgctxt "field:cashbook.recon,end_amount:" +msgid "End Amount" +msgstr "End Amount" + diff --git a/message.xml b/message.xml index 2658a50..71fd465 100644 --- a/message.xml +++ b/message.xml @@ -77,6 +77,22 @@ full copyright notices and license terms. --> The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'. + + Rev + + + Exp + + + from + + + to + + + The current line is managed by the cashbook line '%(recname)s', cashbook: '%(cbook)s'. + + diff --git a/tests/test_book.py b/tests/test_book.py index ed25879..580603b 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -45,6 +45,7 @@ class BookTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -56,6 +57,7 @@ class BookTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -76,6 +78,7 @@ class BookTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -87,6 +90,7 @@ class BookTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -109,6 +113,7 @@ class BookTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -120,6 +125,7 @@ class BookTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -202,6 +208,7 @@ class BookTestCase(ModuleTestCase): types = self.prep_type() company = self.prep_company() category = self.prep_category(cattype='in') + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -228,6 +235,7 @@ class BookTestCase(ModuleTestCase): 'description': 'Test', 'category': category.id, 'bookingtype': 'in', + 'party': party.id, }])], }]) self.assertEqual(book.start_balance, Decimal('1.0')) diff --git a/tests/test_config.py b/tests/test_config.py index 4261c84..2523bdf 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -43,6 +43,16 @@ class ConfigTestCase(ModuleTestCase): self.assertEqual(cfg2.catnamelong, True) return cfg2 + def prep_party(self, name='Party'): + """ new party + """ + Party = Pool().get('party.party') + party, = Party.create([{ + 'name': name, + 'addresses': [('create', [{}])], + }]) + return party + @with_transaction() def test_config_create(self): """ create config diff --git a/tests/test_line.py b/tests/test_line.py index 2c77865..40654f4 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -27,6 +27,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -38,12 +39,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 2), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -51,10 +54,10 @@ class LineTestCase(ModuleTestCase): 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|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') 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|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]') self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1')]), 1) self.assertEqual(Lines.search_count([('rec_name', '=', 'Text 1a')]), 0) @@ -67,9 +70,9 @@ class LineTestCase(ModuleTestCase): # sorting: date -> state -> id self.assertEqual(len(book.lines), 2) - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') self.assertEqual(book.lines[0].state, 'edit') - self.assertEqual(book.lines[1].rec_name, '05/02/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]') self.assertEqual(book.lines[1].state, 'edit') # set to same date @@ -80,17 +83,17 @@ class LineTestCase(ModuleTestCase): }]) # check again book, = Book.search([]) - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') self.assertEqual(book.lines[0].state, 'edit') - self.assertEqual(book.lines[1].rec_name, '05/01/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/01/2022|Rev|1.00 usd|Text 2 [Cat1]') 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|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 2 [Cat1]') self.assertEqual(book.lines[0].state, 'check') - self.assertEqual(book.lines[1].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') self.assertEqual(book.lines[1].state, 'edit') @with_transaction() @@ -104,6 +107,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -115,12 +119,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 1), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -170,6 +176,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() IrDate.today = MagicMock(return_value=date(2022, 6, 1)) book, = Book.create([{ @@ -183,12 +190,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 1), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -221,6 +230,135 @@ class LineTestCase(ModuleTestCase): IrDate.today = MagicMock(return_value=date.today()) + @with_transaction() + def test_line_check_bookingtype_mvout(self): + """ create cashbook + line, bookingtype 'mvout' + """ + pool = Pool() + Book = pool.get('cashbook.book') + Line = pool.get('cashbook.line') + Category = pool.get('cashbook.category') + + types = self.prep_type() + category_in = self.prep_category(cattype='in') + category_out = self.prep_category(name='Out Category', cattype='out') + company = self.prep_company() + party = self.prep_party() + + book2, = Book.create([{ + 'name': 'Book 2', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + }]) + + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'lines': [('create', [{ + 'date': date(2022, 5, 1), + 'description': 'Transfer Out', + 'category': category_out.id, + 'bookingtype': 'mvout', + 'amount': Decimal('1.0'), + 'booktransf': book2.id, + }])], + }]) + self.assertEqual(book.rec_name, 'Book 1 | -1.00 usd | Open') + self.assertEqual(len(book.lines), 1) + self.assertEqual(len(book2.lines), 0) + self.assertEqual(book.lines[0].rec_name, '05/01/2022|to|-1.00 usd|Transfer Out [Book 2 | 0.00 usd | Open]') + self.assertEqual(len(book.lines[0].references), 0) + + # check payee + self.assertEqual(book.lines[0].payee.rec_name, 'Book 2 | 0.00 usd | Open') + self.assertEqual(Line.search_count([('payee', 'ilike', 'book 2%')]), 1) + + # set line to 'checked', this creates the counterpart + Line.wfcheck(list(book.lines)) + + self.assertEqual(len(book.lines), 1) + self.assertEqual(book.lines[0].rec_name, '05/01/2022|to|-1.00 usd|Transfer Out [Book 2 | 1.00 usd | Open]') + self.assertEqual(book.lines[0].state, 'check') + self.assertEqual(len(book.lines[0].references), 1) + self.assertEqual(book.lines[0].reference, None) + self.assertEqual(book.lines[0].references[0].id, book2.lines[0].id) + + self.assertEqual(len(book2.lines), 1) + self.assertEqual(book2.lines[0].rec_name, '05/01/2022|from|1.00 usd|Transfer Out [Book 1 | -1.00 usd | Open]') + self.assertEqual(book2.lines[0].state, 'check') + self.assertEqual(book2.lines[0].reference.rec_name, '05/01/2022|to|-1.00 usd|Transfer Out [Book 2 | 1.00 usd | Open]') + self.assertEqual(len(book2.lines[0].references), 0) + + @with_transaction() + def test_line_check_bookingtype_mvin(self): + """ create cashbook + line, bookingtype 'mvin' + """ + pool = Pool() + Book = pool.get('cashbook.book') + Line = pool.get('cashbook.line') + Category = pool.get('cashbook.category') + + types = self.prep_type() + category_in = self.prep_category(cattype='in') + category_out = self.prep_category(name='Out Category', cattype='out') + company = self.prep_company() + party = self.prep_party() + + book2, = Book.create([{ + 'name': 'Book 2', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + }]) + + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'lines': [('create', [{ + 'date': date(2022, 5, 1), + 'description': 'Transfer In', + 'category': category_in.id, + 'bookingtype': 'mvin', + 'amount': Decimal('1.0'), + 'booktransf': book2.id, + }])], + }]) + self.assertEqual(book.rec_name, 'Book 1 | 1.00 usd | Open') + self.assertEqual(len(book.lines), 1) + self.assertEqual(len(book2.lines), 0) + self.assertEqual(book.lines[0].rec_name, '05/01/2022|from|1.00 usd|Transfer In [Book 2 | 0.00 usd | Open]') + self.assertEqual(len(book.lines[0].references), 0) + + # set line to 'checked', this creates the counterpart + Line.wfcheck(list(book.lines)) + + self.assertEqual(len(book.lines), 1) + self.assertEqual(book.lines[0].rec_name, '05/01/2022|from|1.00 usd|Transfer In [Book 2 | -1.00 usd | Open]') + self.assertEqual(book.lines[0].state, 'check') + self.assertEqual(len(book.lines[0].references), 1) + self.assertEqual(book.lines[0].reference, None) + self.assertEqual(book.lines[0].references[0].id, book2.lines[0].id) + + self.assertEqual(len(book2.lines), 1) + self.assertEqual(book2.lines[0].rec_name, '05/01/2022|to|-1.00 usd|Transfer In [Book 1 | 1.00 usd | Open]') + self.assertEqual(book2.lines[0].state, 'check') + self.assertEqual(book2.lines[0].reference.rec_name, '05/01/2022|from|1.00 usd|Transfer In [Book 2 | -1.00 usd | Open]') + self.assertEqual(len(book2.lines[0].references), 0) + + # tryt wfedit to 'book2.lines[0]' + self.assertRaisesRegex(UserError, + "The current line is managed by the cashbook line '05/01/2022|from|1.00 usd|Transfer In [Book 2 | -1.00 usd | Open]', cashbook: 'Book 1 | 1.00 usd | Open'.", + Line.wfedit, + [book2.lines[0]]) + + Line.wfedit([book.lines[0]]) + self.assertEqual(len(book2.lines), 0) + @with_transaction() def test_line_create_check_debit_credit(self): """ create cashbook + line, check calculation of debit/credit @@ -235,6 +373,14 @@ class LineTestCase(ModuleTestCase): category_in = self.prep_category(cattype='in') category_out = self.prep_category(name='Out Category', cattype='out') company = self.prep_company() + party = self.prep_party() + + book2, = Book.create([{ + 'name': 'Book 2', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + }]) book, = Book.create([{ 'name': 'Book 1', @@ -247,24 +393,28 @@ class LineTestCase(ModuleTestCase): 'category': category_in.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 1), 'description': 'Expense', 'category': category_out.id, 'bookingtype': 'out', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 1), 'description': 'Transfer from', 'category': category_in.id, 'bookingtype': 'mvin', 'amount': Decimal('1.0'), + 'booktransf': book2.id, }, { 'date': date(2022, 6, 1), 'description': 'Transfer to', 'category': category_out.id, 'bookingtype': 'mvout', 'amount': Decimal('1.0'), + 'booktransf': book2.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -276,6 +426,11 @@ class LineTestCase(ModuleTestCase): self.assertEqual(book.lines[0].credit, Decimal('1.0')) self.assertEqual(book.lines[0].debit, Decimal('0.0')) + # check payee + self.assertEqual(book.lines[0].payee.rec_name, 'Party') + self.assertEqual(Line.search_count([('payee', 'ilike', 'party%')]), 2) + self.assertEqual(Line.search_count([('payee', 'ilike', 'book%')]), 2) + 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')) @@ -318,6 +473,7 @@ class LineTestCase(ModuleTestCase): 'category': category_in.id, 'bookingtype': 'mvin', 'amount': Decimal('3.0'), + 'booktransf': book2.id, }]) self.assertEqual(book.lines[0].amount, Decimal('3.0')) self.assertEqual(book.lines[0].bookingtype, 'mvin') @@ -337,6 +493,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() with Transaction().set_context({ 'company': company.id, @@ -370,12 +527,14 @@ class LineTestCase(ModuleTestCase): 'category': category2.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 1), 'description': 'Text 2', 'category': category2.childs[0].id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -407,6 +566,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -418,12 +578,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 2), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -443,6 +605,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -454,12 +617,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 2), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -484,6 +649,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -495,12 +661,14 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 2), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.name, 'Book 1') @@ -529,6 +697,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) usr_lst = ResUser.create([{ 'login': 'frida', @@ -559,6 +728,7 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'), @@ -577,14 +747,15 @@ class LineTestCase(ModuleTestCase): lines = Line.search([]) self.assertEqual(len(lines), 1) self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book | 1.00 usd | Open') - self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 1 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 1 [Cat1]') Line.write(*[ lines, { 'description': 'Test 2', }]) - self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 2 [Cat1]') + self.assertEqual(lines[0].owner_cashbook.rec_name, 'Frida') @with_transaction() def test_line_permission_reviewer(self): @@ -599,6 +770,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) grp_reviewer, = ResGroup.create([{ 'name': 'Cashbook Reviewer', @@ -636,6 +808,7 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'), @@ -650,25 +823,25 @@ class LineTestCase(ModuleTestCase): 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|1.00 usd|Test 1 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 1 [Cat1]') Line.write(*[ lines, { 'description': 'Test 2', }]) - self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 2 [Cat1]') # 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|1.00 usd|Test 2 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 2 [Cat1]') Line.write(*[ lines, { 'description': 'Test 3', }]) - self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 3 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 3 [Cat1]') @with_transaction() def test_line_permission_observer(self): @@ -683,6 +856,7 @@ class LineTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() grp_cashbook, = ResGroup.search([('name', '=', 'Cashbook')]) grp_observer, = ResGroup.create([{ 'name': 'Cashbook Observer', @@ -720,6 +894,7 @@ class LineTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], }]) self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'), @@ -734,7 +909,7 @@ class LineTestCase(ModuleTestCase): 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|1.00 usd|Test 1 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 1 [Cat1]') 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 - ', @@ -750,12 +925,12 @@ class LineTestCase(ModuleTestCase): 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|1.00 usd|Test 1 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 1 [Cat1]') Line.write(*[ lines, { 'description': 'Test 2', }]) - self.assertEqual(lines[0].rec_name, '05/01/2022|1.00 usd|Test 2 [Cat1]') + self.assertEqual(lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Test 2 [Cat1]') # end LineTestCase diff --git a/tests/test_reconciliation.py b/tests/test_reconciliation.py index 998b649..375e4aa 100644 --- a/tests/test_reconciliation.py +++ b/tests/test_reconciliation.py @@ -202,6 +202,7 @@ class ReconTestCase(ModuleTestCase): types = self.prep_type() company = self.prep_company() category = self.prep_category(cattype='in') + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -219,12 +220,14 @@ class ReconTestCase(ModuleTestCase): 'category': category.id, 'description': 'Line 1', 'amount': Decimal('5.0'), + 'party': party.id, }, { 'date': date(2022, 5, 5), 'bookingtype': 'in', 'category': category.id, 'description': 'Line 2', - 'amount': Decimal('7.0'), + 'party': party.id, + 'amount': Decimal('7.0'), },])], }]) self.assertEqual(book.name, 'Book 1') @@ -344,6 +347,7 @@ class ReconTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -355,12 +359,14 @@ class ReconTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 5), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], 'reconciliations': [('create', [{ 'date': date(2022, 5, 28), @@ -371,14 +377,14 @@ class ReconTestCase(ModuleTestCase): self.assertEqual(book.name, 'Book 1') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') - self.assertEqual(book.lines[1].rec_name, '05/05/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/05/2022|Rev|1.00 usd|Text 2 [Cat1]') self.assertEqual(len(book.reconciliations), 1) self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') self.assertEqual(len(book.reconciliations[0].lines), 0) self.assertRaisesRegex(UserError, - "For reconciliation, the line '05/01/2022|1.00 usd|Text 1 [Cat1]' must be in the status 'Check' or 'Done'.", + "For reconciliation, the line '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]' must be in the status 'Check' or 'Done'.", Lines.write, *[ [book.lines[0]], @@ -420,9 +426,9 @@ class ReconTestCase(ModuleTestCase): Reconciliation.wfcheck(list(book.reconciliations)) self.assertEqual(book.reconciliations[0].state, 'check') self.assertEqual(len(book.reconciliations[0].lines), 1) - self.assertEqual(book.reconciliations[0].lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') - self.assertEqual(book.lines[1].rec_name, '06/01/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.reconciliations[0].lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '06/01/2022|Rev|1.00 usd|Text 2 [Cat1]') # move 2nd line into date-range of checked-reconciliation, wf-check Lines.write(*[ @@ -481,6 +487,7 @@ class ReconTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -492,12 +499,14 @@ class ReconTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 6, 5), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], 'reconciliations': [('create', [{ 'date': date(2022, 5, 28), @@ -508,13 +517,13 @@ class ReconTestCase(ModuleTestCase): self.assertEqual(book.name, 'Book 1') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') - self.assertEqual(book.lines[1].rec_name, '06/05/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '06/05/2022|Rev|1.00 usd|Text 2 [Cat1]') Lines.wfcheck([book.lines[0]]) Reconciliation.wfcheck([book.reconciliations[0]]) self.assertEqual(len(book.reconciliations[0].lines), 1) - self.assertEqual(book.reconciliations[0].lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.reconciliations[0].lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') Lines.write(*[ [book.lines[1]], @@ -539,6 +548,7 @@ class ReconTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -550,6 +560,7 @@ class ReconTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], 'reconciliations': [('create', [{ 'date': date(2022, 5, 28), @@ -599,6 +610,7 @@ class ReconTestCase(ModuleTestCase): types = self.prep_type() category = self.prep_category(cattype='in') company = self.prep_company() + party = self.prep_party() book, = Book.create([{ 'name': 'Book 1', 'btype': types.id, @@ -610,12 +622,14 @@ class ReconTestCase(ModuleTestCase): 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }, { 'date': date(2022, 5, 5), 'description': 'Text 2', 'category': category.id, 'bookingtype': 'in', 'amount': Decimal('1.0'), + 'party': party.id, }])], 'reconciliations': [('create', [{ 'date': date(2022, 5, 28), @@ -626,8 +640,8 @@ class ReconTestCase(ModuleTestCase): self.assertEqual(book.name, 'Book 1') self.assertEqual(book.state, 'open') self.assertEqual(len(book.lines), 2) - self.assertEqual(book.lines[0].rec_name, '05/01/2022|1.00 usd|Text 1 [Cat1]') - self.assertEqual(book.lines[1].rec_name, '05/05/2022|1.00 usd|Text 2 [Cat1]') + self.assertEqual(book.lines[0].rec_name, '05/01/2022|Rev|1.00 usd|Text 1 [Cat1]') + self.assertEqual(book.lines[1].rec_name, '05/05/2022|Rev|1.00 usd|Text 2 [Cat1]') self.assertEqual(book.lines[0].reconciliation, None) self.assertEqual(book.lines[1].reconciliation, None) self.assertEqual(len(book.reconciliations), 1) diff --git a/view/line_form.xml b/view/line_form.xml index 8314acb..9ee1a7b 100644 --- a/view/line_form.xml +++ b/view/line_form.xml @@ -35,4 +35,13 @@ full copyright notices and license terms. --> + + +