line: datumsbereich prüfen + test,

abstimmung: vorgänger beachten + test
line: party/transfer-book + test muß noch
This commit is contained in:
Frederik Jaeckel 2022-08-15 17:19:53 +02:00
parent 7a07da852d
commit 30b91cf518
8 changed files with 465 additions and 28 deletions

48
line.py
View file

@ -77,6 +77,23 @@ class Line(Workflow, ModelSQL, ModelView):
required=True, readonly=True, depends=['currency_digits']) required=True, readonly=True, depends=['currency_digits'])
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)), credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
required=True, readonly=True, depends=['currency_digits']) 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, reconciliation = fields.Many2One(string='Reconciliation', readonly=True,
model_name='cashbook.recon', ondelete='SET NULL', model_name='cashbook.recon', ondelete='SET NULL',
domain=[('cashbook.id', '=', Eval('cashbook'))], domain=[('cashbook.id', '=', Eval('cashbook'))],
@ -147,7 +164,36 @@ class Line(Workflow, ModelSQL, ModelView):
def wfcheck(cls, lines): def wfcheck(cls, lines):
""" line is checked """ 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 @classmethod
@ModelView.button @ModelView.button

View file

@ -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." 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" 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." 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, solange die Zeile '%(recname)s' mit einer Abstimmung verbunden ist." 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" msgctxt "model:ir.message,text:msg_line_deny_write"
msgid "The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed." 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." msgid "The date range overlaps with another reconciliation."
msgstr "Der Datumsbereich überschneidet sich mit einer anderen Abstimmung." 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 # # res.group #
@ -474,6 +490,10 @@ msgctxt "field:cashbook.line,reconciliation:"
msgid "Reconciliation" msgid "Reconciliation"
msgstr "Abstimmung" msgstr "Abstimmung"
msgctxt "field:cashbook.line,party:"
msgid "Party"
msgstr "Partei"
################# #################
# cashbook.type # # cashbook.type #
@ -809,3 +829,7 @@ msgstr "Anfangsbetrag"
msgctxt "field:cashbook.recon,end_amount:" msgctxt "field:cashbook.recon,end_amount:"
msgid "End Amount" msgid "End Amount"
msgstr "Endbetrag" msgstr "Endbetrag"
msgctxt "field:cashbook.recon,predecessor:"
msgid "Predecessor"
msgstr "Vorgänger"

View file

@ -33,7 +33,7 @@ full copyright notices and license terms. -->
<field name="text">The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed.</field> <field name="text">The cashbook line '%(recname)s' is '%(state_txt)s' and cannot be changed.</field>
</record> </record>
<record model="ir.message" id="msg_line_deny_stateedit_with_recon"> <record model="ir.message" id="msg_line_deny_stateedit_with_recon">
<field name="text">The status cannot be changed to 'Edit' as long as the line '%(recname)s' is associated with a reconciliation.</field> <field name="text">The status cannot be changed to 'Edit' while the line '%(recname)s' is associated with a reconciliation.</field>
</record> </record>
<record model="ir.message" id="msg_recon_deny_delete1"> <record model="ir.message" id="msg_recon_deny_delete1">
<field name="text">The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'.</field> <field name="text">The reconciliation '%(recontxt)s' cannot be deleted because the Cashbook '%(bookname)s' is in state '%(bookstate)s'.</field>
@ -65,6 +65,18 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_recon_err_overlap"> <record model="ir.message" id="msg_recon_err_overlap">
<field name="text">The date range overlaps with another reconciliation.</field> <field name="text">The date range overlaps with another reconciliation.</field>
</record> </record>
<record model="ir.message" id="msg_line_err_write_to_reconciled">
<field name="text">For the date '%(datetxt)s' there is already a completed reconciliation. Use a different date.</field>
</record>
<record model="ir.message" id="msg_recon_lines_no_linked">
<field name="text">In the date range from %(date_from)s to %(date_to)s, there are still cashbook lines that do not belong to any reconciliation.</field>
</record>
<record model="ir.message" id="msg_recon_date_from_to_mismatch">
<field name="text">The start date '%(datefrom)s' of the current reconciliation '%(recname)s' must correspond to the end date '%(dateto)s' of the predecessor.</field>
</record>
<record model="ir.message" id="msg_recon_predecessor_not_done">
<field name="text">The predecessor '%(recname_p)s' must be in the 'Done' state before you can check the current reconciliation '%(recname_c)s'.</field>
</record>
</data> </data>
</tryton> </tryton>

View file

@ -81,6 +81,9 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
string="Currency"), 'on_change_with_currency') string="Currency"), 'on_change_with_currency')
currency_digits = fields.Function(fields.Integer(string='Currency Digits'), currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
'on_change_with_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, state = fields.Selection(string='State', required=True, readonly=True,
select=True, selection=sel_reconstate) select=True, selection=sel_reconstate)
@ -207,16 +210,25 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
for reconciliation in reconciliations: for reconciliation in reconciliations:
values = {} values = {}
# get start_amount: end_amount of predecessor if reconciliation.predecessor:
pre_recon = Recon.search([ # predecessor must be 'done'
('cashbook.id', '=', reconciliation.cashbook.id), if reconciliation.predecessor.state != 'done':
('date_to', '<=', reconciliation.date_from), raise UserError(gettext(
('state', 'in', ['check', 'done']), 'cashbook.msg_recon_predecessor_not_done',
], order=[('date_to', 'DESC')], limit=1) recname_p = reconciliation.predecessor.rec_name,
if len(pre_recon) > 0: recname_c = reconciliation.rec_name,
values['start_amount'] = pre_recon[0].end_amount ))
# 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 : else :
# not found, use 'start_balance' of cashbook
values['start_amount'] = reconciliation.cashbook.start_balance values['start_amount'] = reconciliation.cashbook.start_balance
values['end_amount'] = values['start_amount'] values['end_amount'] = values['start_amount']
@ -253,13 +265,32 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
for reconciliation in reconciliations: for reconciliation in reconciliations:
to_wfdone_line.extend(list(reconciliation.lines)) 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: if len(to_wfdone_line) > 0:
Line.wfdone(to_wfdone_line) Line.wfdone(to_wfdone_line)
def get_rec_name(self, name): def get_rec_name(self, name):
""" short + 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 '-', '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 '-', '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), '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') IrDate = Pool().get('ir.date')
return IrDate.today() 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') @fields.depends('cashbook', '_parent_cashbook.currency')
def on_change_with_currency(self, name=None): def on_change_with_currency(self, name=None):
""" currency of cashbook """ currency of cashbook
@ -314,7 +360,16 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
def create(cls, vlist): def create(cls, vlist):
""" add debit/credit """ add debit/credit
""" """
Recon = Pool().get('cashbook.recon')
for values in vlist: 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( cls.check_overlap_dates(
values.get('date_from', None), values.get('date_from', None),
values.get('date_to', None), values.get('date_to', None),

View file

@ -47,15 +47,10 @@ class ReconTestCase(ModuleTestCase):
'date_to': date(2022, 6, 30), 'date_to': date(2022, 6, 30),
'cashbook': book.id, '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' # 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(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]') 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, self.assertRaisesRegex(UserError,
'The date range overlaps with another reconciliation.', 'The date range overlaps with another reconciliation.',
Reconciliation.create, Reconciliation.create,
@ -159,13 +154,183 @@ class ReconTestCase(ModuleTestCase):
recon3, = Reconciliation.create([{ recon3, = Reconciliation.create([{
'date': date(2022, 4, 17), 'date': date(2022, 4, 17),
'date_from': date(2022, 4, 1), 'date_from': date(2022, 6, 1),
'date_to': date(2022, 4, 15), 'date_to': date(2022, 6, 15),
'cashbook': book.id, 'cashbook': book.id,
}]) }])
self.assertEqual(recon1.rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') 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(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() @with_transaction()
def test_recon_create_check_line_add_to_recon(self): def test_recon_create_check_line_add_to_recon(self):
@ -235,6 +400,133 @@ class ReconTestCase(ModuleTestCase):
Lines.wfedit, Lines.wfedit,
[book.lines[0]]) [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() @with_transaction()
def test_recon_check_deny_delete(self): def test_recon_check_deny_delete(self):
""" create, booklines, reconciliation, try delete """ create, booklines, reconciliation, try delete
@ -353,8 +645,8 @@ class ReconTestCase(ModuleTestCase):
Lines.wfcheck(book.lines) Lines.wfcheck(book.lines)
Reconciliation.wfcheck(list(book.reconciliations)) Reconciliation.wfcheck(list(book.reconciliations))
self.assertEqual(len(book.reconciliations[0].lines), 2) 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[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 - 0.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 # check --> edit
Reconciliation.wfedit(list(book.reconciliations)) Reconciliation.wfedit(list(book.reconciliations))

View file

@ -3,6 +3,7 @@ version=6.0.0
depends: depends:
res res
currency currency
party
company company
xml: xml:
icon.xml icon.xml

View file

@ -7,7 +7,12 @@ full copyright notices and license terms. -->
<separator name="state" colspan="2" string="State"/> <separator name="state" colspan="2" string="State"/>
<field name="cashbook" colspan="2"/> <field name="cashbook" colspan="2"/>
<label id="lab1" string=" " colspan="2"/> <group id="grpparty" colspan="2" col="4">
<label name="party"/>
<field name="party"/>
<label name="booktransf"/>
<field name="booktransf"/>
</group>
<field name="state"/> <field name="state"/>
<group id="grpstate" col="2"> <group id="grpstate" col="2">

View file

@ -5,6 +5,8 @@ full copyright notices and license terms. -->
<tree> <tree>
<field name="cashbook" tree_invisible="1"/> <field name="cashbook" tree_invisible="1"/>
<field name="date"/> <field name="date"/>
<field name="party"/>
<field name="booktransf"/>
<field name="category_view"/> <field name="category_view"/>
<field name="description" expand="1"/> <field name="description" expand="1"/>
<field name="credit" sum="Credit"/> <field name="credit" sum="Credit"/>