From 30b91cf5188f90a38d6ca0be3c0cc9a3e3eb448f Mon Sep 17 00:00:00 2001 From: Frederik Jaeckel Date: Mon, 15 Aug 2022 17:19:53 +0200 Subject: [PATCH] =?UTF-8?q?line:=20datumsbereich=20pr=C3=BCfen=20+=20test,?= =?UTF-8?q?=20abstimmung:=20vorg=C3=A4nger=20beachten=20+=20test=20line:?= =?UTF-8?q?=20party/transfer-book=20+=20test=20mu=C3=9F=20noch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- line.py | 48 +++++- locale/de.po | 28 ++- message.xml | 14 +- reconciliation.py | 75 +++++++-- tests/test_reconciliation.py | 318 +++++++++++++++++++++++++++++++++-- tryton.cfg | 1 + view/line_form.xml | 7 +- view/line_list.xml | 2 + 8 files changed, 465 insertions(+), 28 deletions(-) diff --git a/line.py b/line.py index 5cdc934..64734a5 100644 --- a/line.py +++ b/line.py @@ -77,6 +77,23 @@ class Line(Workflow, ModelSQL, ModelView): required=True, readonly=True, depends=['currency_digits']) credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)), required=True, readonly=True, depends=['currency_digits']) + + booktransf = fields.Many2One(string='Transfer Cashbook', + ondelete='RESTRICT', model_name='cashbook.book', + domain=[('cashbook.owner.id', '=', Eval('context',{}).get('user', -1))], + states={ + 'readonly': STATES['readonly'], + 'invisible': ~Eval('bookingtype', '').in_(['mvin', 'mvout']), + 'required': Eval('bookingtype', '').in_(['mvin', 'mvout']), + }, depends=DEPENDS+['bookingtype']) + party = fields.Many2One(string='Party', model_name='party.party', + ondelete='RESTRICT', + states={ + 'readonly': STATES['readonly'], + 'invisible': ~Eval('bookingtype', '').in_(['in', 'out']), + 'required': Eval('bookingtype', '').in_(['in', 'out']), + }, depends=DEPENDS+['bookingtype']) + reconciliation = fields.Many2One(string='Reconciliation', readonly=True, model_name='cashbook.recon', ondelete='SET NULL', domain=[('cashbook.id', '=', Eval('cashbook'))], @@ -147,7 +164,36 @@ class Line(Workflow, ModelSQL, ModelView): def wfcheck(cls, lines): """ line is checked """ - pass + pool = Pool() + Recon = pool.get('cashbook.recon') + + for line in lines: + # deny if date is in range of existing reconciliation + # allow cashbook-line at range-limits + if Recon.search_count([ + ('state', 'in', ['check', 'done']), + ('cashbook.id', '=', line.cashbook.id), + ('date_from', '<', line.date), + ('date_to', '>', line.date), + ]) > 0: + raise UserError(gettext( + 'cashbook.msg_line_err_write_to_reconciled', + datetxt = Report.format_date(line.date), + )) + # deny if date is at reconciliation limits and two + # reconciliations exist + if Recon.search_count([ + ('state', 'in', ['check', 'done']), + ('cashbook.id', '=', line.cashbook.id), + ['OR', + ('date_from', '=', line.date), + ('date_to', '=', line.date), + ] + ]) > 1: + raise UserError(gettext( + 'cashbook.msg_line_err_write_to_reconciled', + datetxt = Report.format_date(line.date), + )) @classmethod @ModelView.button diff --git a/locale/de.po b/locale/de.po index 8c8fb1d..ea9a08d 100644 --- a/locale/de.po +++ b/locale/de.po @@ -39,8 +39,8 @@ msgid "The cashbook line '%(linetxt)s' cannot be deleted, its in state '%(linest msgstr "Die Kassenbuchzeile '%(linetxt)s' kann nicht gelöscht werden, da sie im Status '%(linestate)s' ist." 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 "Der Status kann nicht in 'Bearbeiten' geändert werden, solange die Zeile '%(recname)s' mit einer Abstimmung verbunden ist." +msgid "The status cannot be changed to 'Edit' while the line '%(recname)s' is associated with a reconciliation." +msgstr "Der Status kann nicht in 'Bearbeiten' geändert werden, während die Zeile '%(recname)s' mit einer Abstimmung verbunden ist." msgctxt "model:ir.message,text:msg_line_deny_write" msgid "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed." @@ -86,6 +86,22 @@ msgctxt "model:ir.message,text:msg_recon_err_overlap" msgid "The date range overlaps with another reconciliation." msgstr "Der Datumsbereich überschneidet sich mit einer anderen Abstimmung." +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 "Für das Datum '%(datetxt)s' gibt es bereits eine abgeschlossene Abstimmung. Verwenden Sie ein anderes Datum." + +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 "Im Datumsbereich von %(date_from)s bis %(date_to)s existieren noch Kassenbuchzeilen, die zu keiner Abstimmung gehören." + +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 "Das Anfangsdatum '%(datefrom)s' der aktuellen Abstimmung '%(recname)s' muß dem Endedatum '%(dateto)s' des Vorgängers entsprechen." + +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." + ############# # res.group # @@ -474,6 +490,10 @@ msgctxt "field:cashbook.line,reconciliation:" msgid "Reconciliation" msgstr "Abstimmung" +msgctxt "field:cashbook.line,party:" +msgid "Party" +msgstr "Partei" + ################# # cashbook.type # @@ -809,3 +829,7 @@ msgstr "Anfangsbetrag" msgctxt "field:cashbook.recon,end_amount:" msgid "End Amount" msgstr "Endbetrag" + +msgctxt "field:cashbook.recon,predecessor:" +msgid "Predecessor" +msgstr "Vorgänger" diff --git a/message.xml b/message.xml index 236efe0..2658a50 100644 --- a/message.xml +++ b/message.xml @@ -33,7 +33,7 @@ full copyright notices and license terms. --> The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed. - The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation. + The status cannot be changed to 'Edit' while the line '%(recname)s' is associated with a reconciliation. The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'. @@ -65,6 +65,18 @@ full copyright notices and license terms. --> The date range overlaps with another reconciliation. + + For the date '%(datetxt)s' there is already a completed reconciliation. Use a different date. + + + In the date range from %(date_from)s to %(date_to)s, there are still cashbook lines that do not belong to any reconciliation. + + + The start date '%(datefrom)s' of the current reconciliation '%(recname)s' must correspond to the end date '%(dateto)s' of the predecessor. + + + The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'. + diff --git a/reconciliation.py b/reconciliation.py index 55fe50c..f74cf2f 100644 --- a/reconciliation.py +++ b/reconciliation.py @@ -81,6 +81,9 @@ class Reconciliation(Workflow, ModelSQL, ModelView): string="Currency"), 'on_change_with_currency') currency_digits = fields.Function(fields.Integer(string='Currency Digits'), 'on_change_with_currency_digits') + predecessor = fields.Function(fields.Many2One(string='Predecessor', readonly=True, + model_name='cashbook.recon'), + 'on_change_with_predecessor') state = fields.Selection(string='State', required=True, readonly=True, select=True, selection=sel_reconstate) @@ -207,16 +210,25 @@ class Reconciliation(Workflow, ModelSQL, ModelView): for reconciliation in reconciliations: values = {} - # get start_amount: end_amount of predecessor - pre_recon = Recon.search([ - ('cashbook.id', '=', reconciliation.cashbook.id), - ('date_to', '<=', reconciliation.date_from), - ('state', 'in', ['check', 'done']), - ], order=[('date_to', 'DESC')], limit=1) - if len(pre_recon) > 0: - values['start_amount'] = pre_recon[0].end_amount + if reconciliation.predecessor: + # predecessor must be 'done' + if reconciliation.predecessor.state != 'done': + raise UserError(gettext( + 'cashbook.msg_recon_predecessor_not_done', + recname_p = reconciliation.predecessor.rec_name, + recname_c = reconciliation.rec_name, + )) + + # check if current.date_from == predecessor.date_to + if reconciliation.predecessor.date_to != reconciliation.date_from: + raise UserError(gettext( + 'cashbook.msg_recon_date_from_to_mismatch', + datefrom = Report.format_date(reconciliation.date_from), + dateto = Report.format_date(reconciliation.predecessor.date_to), + recname = reconciliation.rec_name, + )) + values['start_amount'] = reconciliation.predecessor.end_amount else : - # not found, use 'start_balance' of cashbook values['start_amount'] = reconciliation.cashbook.start_balance values['end_amount'] = values['start_amount'] @@ -253,13 +265,32 @@ class Reconciliation(Workflow, ModelSQL, ModelView): for reconciliation in reconciliations: to_wfdone_line.extend(list(reconciliation.lines)) + # deny if there are lines not linked to reconciliation + if Line.search_count([ + ('cashbook.id', '=', reconciliation.cashbook.id), + ('reconciliation', '=', None), + ['OR', + [ # lines inside of date-range + ('date', '>', reconciliation.date_from), + ('date', '<', reconciliation.date_to), + ], + # lines at from-date must relate to a reconciliation + ('date', '=', reconciliation.date_from), + ], + ]) > 0: + raise UserError(gettext( + 'cashbook.msg_recon_lines_no_linked', + date_from = Report.format_date(reconciliation.date_from), + date_to = Report.format_date(reconciliation.date_to), + )) + if len(to_wfdone_line) > 0: Line.wfdone(to_wfdone_line) def get_rec_name(self, name): """ short + name """ - return '%(from)s - %(to)s | %(start_amount)s %(symbol)s - %(start_amount)s %(symbol)s [%(num)s]' % { + return '%(from)s - %(to)s | %(start_amount)s %(symbol)s - %(end_amount)s %(symbol)s [%(num)s]' % { 'from': Report.format_date(self.date_from, None) if self.date_from is not None else '-', 'to': Report.format_date(self.date_to, None) if self.date_to is not None else '-', 'start_amount': Report.format_number(self.start_amount or 0.0, None), @@ -287,6 +318,21 @@ class Reconciliation(Workflow, ModelSQL, ModelView): IrDate = Pool().get('ir.date') return IrDate.today() + @fields.depends('cashbook', '_parent_cashbook.id', 'date_from') + def on_change_with_predecessor(self, name=None): + """ get predecessor + """ + Recon = Pool().get('cashbook.recon') + + if self.cashbook: + if self.date_from is not None: + reconciliations = Recon.search([ + ('cashbook.id', '=', self.cashbook.id), + ('date_from', '<', self.date_from), + ], order=[('date_from', 'DESC')], limit=1) + if len(reconciliations) > 0: + return reconciliations[0].id + @fields.depends('cashbook', '_parent_cashbook.currency') def on_change_with_currency(self, name=None): """ currency of cashbook @@ -314,7 +360,16 @@ class Reconciliation(Workflow, ModelSQL, ModelView): def create(cls, vlist): """ add debit/credit """ + Recon = Pool().get('cashbook.recon') + for values in vlist: + # set date_from date_to of predecessor + recons = Recon.search([ + ('cashbook.id', '=', values.get('cashbook', -1)), + ], order=[('date_to', 'DESC')], limit=1) + if len(recons) > 0: + values['date_from'] = recons[0].date_to + cls.check_overlap_dates( values.get('date_from', None), values.get('date_to', None), diff --git a/tests/test_reconciliation.py b/tests/test_reconciliation.py index 6e12b81..998b649 100644 --- a/tests/test_reconciliation.py +++ b/tests/test_reconciliation.py @@ -47,15 +47,10 @@ class ReconTestCase(ModuleTestCase): 'date_to': date(2022, 6, 30), 'cashbook': book.id, }]) - self.assertEqual(recon2.rec_name, '06/01/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') + # updated by create to date_to of predecessor + self.assertEqual(recon2.rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') # same day for end of '0' and start of '1' - Reconciliation.write(*[ - [recon2], - { - 'date_from': date(2022, 5, 31), - }, - ]) self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') self.assertEqual(recon2.rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') @@ -146,7 +141,7 @@ class ReconTestCase(ModuleTestCase): }, ]) - # overlap at create + # overlap at .create() self.assertRaisesRegex(UserError, 'The date range overlaps with another reconciliation.', Reconciliation.create, @@ -159,13 +154,183 @@ class ReconTestCase(ModuleTestCase): recon3, = Reconciliation.create([{ 'date': date(2022, 4, 17), - 'date_from': date(2022, 4, 1), - 'date_to': date(2022, 4, 15), + 'date_from': date(2022, 6, 1), + 'date_to': date(2022, 6, 15), 'cashbook': book.id, }]) self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') self.assertEqual(recon2.rec_name, '04/15/2022 - 05/01/2022 | 0.00 usd - 0.00 usd [0]') - self.assertEqual(recon3.rec_name, '04/01/2022 - 04/15/2022 | 0.00 usd - 0.00 usd [0]') + self.assertEqual(recon3.rec_name, '05/31/2022 - 06/15/2022 | 0.00 usd - 0.00 usd [0]') + + @with_transaction() + def test_recon_set_start_amount_by_cashbook(self): + """ set stat-amount from cashbook-setting + """ + pool = Pool() + Book = pool.get('cashbook.book') + Reconciliation = pool.get('cashbook.recon') + + types = self.prep_type() + company = self.prep_company() + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'start_balance': Decimal('12.50'), + 'reconciliations': [('create', [{ + 'date': date(2022, 5, 28), + 'date_from': date(2022, 5, 1), + 'date_to': date(2022, 5, 31), + }])], + }]) + self.assertEqual(book.name, 'Book 1') + self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') + + Reconciliation.wfcheck(list(book.reconciliations)) + self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 12.50 usd - 12.50 usd [0]') + + @with_transaction() + def test_recon_set_start_amount_by_predecessor(self): + """ set stat-amount from end_amount of predecessor + """ + pool = Pool() + Book = pool.get('cashbook.book') + Lines = pool.get('cashbook.line') + Reconciliation = pool.get('cashbook.recon') + + types = self.prep_type() + company = self.prep_company() + category = self.prep_category(cattype='in') + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'start_balance': Decimal('12.50'), + 'reconciliations': [('create', [{ + 'date': date(2022, 5, 28), + 'date_from': date(2022, 5, 1), + 'date_to': date(2022, 5, 31), + }])], + 'lines': [('create', [{ + 'date': date(2022, 5, 5), + 'bookingtype': 'in', + 'category': category.id, + 'description': 'Line 1', + 'amount': Decimal('5.0'), + }, { + 'date': date(2022, 5, 5), + 'bookingtype': 'in', + 'category': category.id, + 'description': 'Line 2', + 'amount': Decimal('7.0'), + },])], + }]) + self.assertEqual(book.name, 'Book 1') + 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) + + Lines.wfcheck(list(book.lines)) + Reconciliation.wfcheck(list(book.reconciliations)) + + self.assertEqual(book.reconciliations[0].state, 'check') + self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 12.50 usd - 24.50 usd [2]') + Reconciliation.wfdone(list(book.reconciliations)) + self.assertEqual(book.reconciliations[0].state, 'done') + + recons = Reconciliation.create([{ + 'cashbook': book.id, + 'date_from': date(2022, 5, 31), + 'date_to': date(2022, 6, 30), + }]) + self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') + Reconciliation.wfcheck(recons) + self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 24.50 usd - 24.50 usd [0]') + + @with_transaction() + def test_recon_predecessor_done(self): + """ predecessor must be done + """ + pool = Pool() + Book = pool.get('cashbook.book') + Reconciliation = pool.get('cashbook.recon') + + types = self.prep_type() + company = self.prep_company() + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'reconciliations': [('create', [{ + 'date': date(2022, 5, 28), + 'date_from': date(2022, 5, 1), + 'date_to': date(2022, 5, 31), + }])], + }]) + self.assertEqual(book.name, 'Book 1') + self.assertEqual(book.state, 'open') + Reconciliation.wfcheck(list(book.reconciliations)) + self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') + self.assertEqual(book.reconciliations[0].state, 'check') + + recons = Reconciliation.create([{ + 'cashbook': book.id, + 'date_from': date(2022, 5, 31), + 'date_to': date(2022, 6, 30), + }]) + self.assertRaisesRegex(UserError, + "The predecessor '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]' must be in the 'Done' state before you can check the current reconciliation '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]'.", + Reconciliation.wfcheck, + recons) + + @with_transaction() + def test_recon_autoset_date_from(self): + """ create reconciliation, check: set date_from to end of predecessor + """ + pool = Pool() + Book = pool.get('cashbook.book') + Reconciliation = pool.get('cashbook.recon') + + types = self.prep_type() + company = self.prep_company() + book, = Book.create([{ + 'name': 'Book 1', + 'btype': types.id, + 'company': company.id, + 'currency': company.currency.id, + 'reconciliations': [('create', [{ + 'date': date(2022, 5, 28), + 'date_from': date(2022, 5, 1), + 'date_to': date(2022, 5, 31), + }])], + }]) + self.assertEqual(book.name, 'Book 1') + self.assertEqual(book.state, 'open') + Reconciliation.wfcheck([book.reconciliations[0]]) + Reconciliation.wfdone([book.reconciliations[0]]) + + r2, = Reconciliation.create([{ + 'cashbook': book.id, + 'date_from': date(2022, 6, 10), + 'date_to': date(2022, 6, 30), + }]) + self.assertEqual(r2.rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') + + # update 'date_from' to wrong value + Reconciliation.write(*[ + [r2], + { + 'date_from': date(2022, 6, 1), + }]) + self.assertEqual(r2.rec_name, '06/01/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') + + self.assertRaisesRegex(UserError, + "The start date '06/01/2022' of the current reconciliation '06/01/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]' must correspond to the end date '05/31/2022' of the predecessor.", + Reconciliation.wfcheck, + [r2]) @with_transaction() def test_recon_create_check_line_add_to_recon(self): @@ -235,6 +400,133 @@ class ReconTestCase(ModuleTestCase): Lines.wfedit, [book.lines[0]]) + # unlink lines from reconciliation + self.assertEqual(book.reconciliations[0].state, 'edit') + self.assertEqual(len(book.reconciliations[0].lines), 2) + Reconciliation.wfcheck(list(book.reconciliations)) + Reconciliation.wfedit(list(book.reconciliations)) + self.assertEqual(book.reconciliations[0].state, 'edit') + self.assertEqual(len(book.reconciliations[0].lines), 0) + + # move date of 2nd line to june 1 + # set reconciliation to 'check' + Lines.wfedit([book.lines[1]]) + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 6, 1), + }]) + # check reconciliation, this linkes the 1st line by date + 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]') + + # move 2nd line into date-range of checked-reconciliation, wf-check + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 5, 20), + }]) + self.assertRaisesRegex(UserError, + "For the date '05/20/2022' there is already a completed reconciliation. Use a different date.", + Lines.wfcheck, + [book.lines[1]]) + + # set date to end of date-range of reconciliation + # should work + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 5, 31), + }]) + Lines.wfcheck([book.lines[1]]) # ok + Lines.wfedit([book.lines[1]]) + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 7, 1), + }]) + + # add 2nd reconciliation + recon2, = Reconciliation.create([{ + 'cashbook': book.id, + 'date_from': date(2022, 5, 31), + 'date_to': date(2022, 6, 30), + }]) + Reconciliation.wfdone([book.reconciliations[0]]) + Reconciliation.wfcheck([recon2]) + + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 5, 31), + }]) + self.assertRaisesRegex(UserError, + "For the date '05/31/2022' there is already a completed reconciliation. Use a different date.", + Lines.wfcheck, + [book.lines[1]]) + + @with_transaction() + def test_recon_check_wfcheck_missing_lines(self): + """ create, booklines, check missing line at wfcheck + """ + pool = Pool() + Book = pool.get('cashbook.book') + Lines = pool.get('cashbook.line') + Reconciliation = pool.get('cashbook.recon') + + types = self.prep_type() + category = self.prep_category(cattype='in') + 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, 5), + 'description': 'Text 2', + 'category': category.id, + 'bookingtype': 'in', + 'amount': Decimal('1.0'), + }])], + 'reconciliations': [('create', [{ + 'date': date(2022, 5, 28), + 'date_from': date(2022, 5, 1), + 'date_to': date(2022, 5, 31), + }])], + }]) + 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]') + + 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]') + + Lines.write(*[ + [book.lines[1]], + { + 'date': date(2022, 5, 15), + }]) + + self.assertRaisesRegex(UserError, + "In the date range from 05/01/2022 to 05/31/2022, there are still cashbook lines that do not belong to any reconciliation.", + Reconciliation.wfdone, + [book.reconciliations[0]]) + @with_transaction() def test_recon_check_deny_delete(self): """ create, booklines, reconciliation, try delete @@ -353,8 +645,8 @@ class ReconTestCase(ModuleTestCase): Lines.wfcheck(book.lines) Reconciliation.wfcheck(list(book.reconciliations)) self.assertEqual(len(book.reconciliations[0].lines), 2) - self.assertEqual(book.lines[0].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [2]') - self.assertEqual(book.lines[1].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [2]') + self.assertEqual(book.lines[0].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 2.00 usd [2]') + self.assertEqual(book.lines[1].reconciliation.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 2.00 usd [2]') # check --> edit Reconciliation.wfedit(list(book.reconciliations)) diff --git a/tryton.cfg b/tryton.cfg index 4ecb482..5a132ab 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -3,6 +3,7 @@ version=6.0.0 depends: res currency + party company xml: icon.xml diff --git a/view/line_form.xml b/view/line_form.xml index aa45808..8314acb 100644 --- a/view/line_form.xml +++ b/view/line_form.xml @@ -7,7 +7,12 @@ full copyright notices and license terms. --> -