[IMP] account: define the m2m relation between bank statement lines and account.move.line instead of account.move
This is needed in order to support use cases where we have several account.move.line on an account_id of internal_type 'bank' inside a single account.move. Indeed, each journal item should be linked to a bank statement line in order to disappear from the bank reconciliation widget and, previously, setting the reference to the second bank statement line was removing the first as both info had to be filled in a single m2o. OPW: 696842
This commit is contained in:
@@ -238,7 +238,8 @@ class AccountBankStatement(models.Model):
|
||||
st_line.fast_counterpart_creation()
|
||||
elif not st_line.journal_entry_ids.ids:
|
||||
raise UserError(_('All the account entries lines must be processed in order to close the statement.'))
|
||||
moves = (moves | st_line.journal_entry_ids)
|
||||
for aml in st_line.journal_entry_ids:
|
||||
moves |= aml.move_id
|
||||
if moves:
|
||||
moves.post()
|
||||
statement.message_post(body=_('Statement %s confirmed, journal items were created.') % (statement.name,))
|
||||
@@ -280,13 +281,12 @@ class AccountBankStatement(models.Model):
|
||||
Return ids of statement lines left to reconcile and other data for the reconciliation widget.
|
||||
"""
|
||||
statements = self
|
||||
bsl_obj = self.env['account.bank.statement.line']
|
||||
# NB : The field account_id can be used at the statement line creation/import to avoid the reconciliation process on it later on,
|
||||
# this is why we filter out statements lines where account_id is set
|
||||
|
||||
sql_query = """SELECT stl.id
|
||||
FROM account_bank_statement_line stl
|
||||
WHERE account_id IS NULL AND not exists (select 1 from account_move m where m.statement_line_id = stl.id)
|
||||
sql_query = """SELECT stl.id
|
||||
FROM account_bank_statement_line stl
|
||||
WHERE account_id IS NULL AND not exists (select 1 from account_move_line aml where aml.statement_line_id = stl.id)
|
||||
AND company_id = %s
|
||||
"""
|
||||
params = (self.env.user.company_id.id,)
|
||||
@@ -362,10 +362,10 @@ class AccountBankStatementLine(models.Model):
|
||||
note = fields.Text(string='Notes')
|
||||
sequence = fields.Integer(index=True, help="Gives the sequence order when displaying a list of bank statement lines.", default=1)
|
||||
company_id = fields.Many2one('res.company', related='statement_id.company_id', string='Company', store=True, readonly=True)
|
||||
journal_entry_ids = fields.One2many('account.move', 'statement_line_id', 'Journal Entries', copy=False, readonly=True)
|
||||
journal_entry_ids = fields.One2many('account.move.line', 'statement_line_id', 'Journal Items', copy=False, readonly=True)
|
||||
amount_currency = fields.Monetary(help="The amount expressed in an optional other currency if it is a multi-currency entry.")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', help="The optional other currency if it is a multi-currency entry.")
|
||||
state = fields.Selection(related='statement_id.state' , string='Status', readonly=True)
|
||||
state = fields.Selection(related='statement_id.state', string='Status', readonly=True)
|
||||
move_name = fields.Char(string='Journal Entry Name', readonly=True,
|
||||
default=False, copy=False,
|
||||
help="Technical field holding the number given to the journal entry, automatically set when the statement line is reconciled then stored to set the same number again if the line is cancelled, set to draft and re-processed again.")
|
||||
@@ -409,32 +409,30 @@ class AccountBankStatementLine(models.Model):
|
||||
|
||||
@api.multi
|
||||
def button_cancel_reconciliation(self):
|
||||
moves_to_unbind = self.env['account.move']
|
||||
moves_to_cancel = self.env['account.move']
|
||||
aml_to_unbind = self.env['account.move.line']
|
||||
aml_to_cancel = self.env['account.move']
|
||||
payment_to_unreconcile = self.env['account.payment']
|
||||
payment_to_cancel = self.env['account.payment']
|
||||
for st_line in self:
|
||||
moves_to_unbind |= st_line.journal_entry_ids
|
||||
aml_to_unbind |= st_line.journal_entry_ids
|
||||
for move in st_line.journal_entry_ids:
|
||||
for line in move.line_ids:
|
||||
payment_to_unreconcile |= line.payment_id
|
||||
if st_line.move_name and line.payment_id.payment_reference == st_line.move_name:
|
||||
#there can be several moves linked to a statement line but maximum one created by the line itself
|
||||
moves_to_cancel |= st_line.journal_entry_ids
|
||||
aml_to_cancel |= st_line.journal_entry_ids
|
||||
payment_to_cancel |= line.payment_id
|
||||
moves_to_unbind = moves_to_unbind - moves_to_cancel
|
||||
aml_to_unbind = aml_to_unbind - aml_to_cancel
|
||||
payment_to_unreconcile = payment_to_unreconcile - payment_to_cancel
|
||||
|
||||
if moves_to_unbind:
|
||||
moves_to_unbind.write({'statement_line_id': False})
|
||||
for move in moves_to_unbind:
|
||||
move.line_ids.filtered(lambda x:x.statement_id == st_line.statement_id).write({'statement_id': False})
|
||||
if aml_to_unbind:
|
||||
aml_to_unbind.write({'statement_line_id': False})
|
||||
if payment_to_unreconcile:
|
||||
payment_to_unreconcile.unreconcile()
|
||||
|
||||
if moves_to_cancel:
|
||||
for move in moves_to_cancel:
|
||||
move.line_ids.remove_move_reconcile()
|
||||
if aml_to_cancel:
|
||||
aml_to_cancel.remove_move_reconcile()
|
||||
moves_to_cancel = set([x.move_id for x in aml_to_cancel])
|
||||
moves_to_cancel.button_cancel()
|
||||
moves_to_cancel.unlink()
|
||||
if payment_to_cancel:
|
||||
@@ -559,7 +557,7 @@ class AccountBankStatementLine(models.Model):
|
||||
"""
|
||||
# Blue lines = payment on bank account not assigned to a statement yet
|
||||
reconciliation_aml_accounts = [self.journal_id.default_credit_account_id.id, self.journal_id.default_debit_account_id.id]
|
||||
domain_reconciliation = ['&', '&', ('statement_id', '=', False), ('account_id', 'in', reconciliation_aml_accounts), ('payment_id','<>', False)]
|
||||
domain_reconciliation = ['&', '&', ('statement_line_id', '=', False), ('account_id', 'in', reconciliation_aml_accounts), ('payment_id','<>', False)]
|
||||
|
||||
# Black lines = unreconciled & (not linked to a payment or open balance created by statement
|
||||
domain_matching = ['&', ('reconciled', '=', False), '|', ('payment_id','=',False), ('statement_id', '<>', False)]
|
||||
@@ -740,7 +738,6 @@ class AccountBankStatementLine(models.Model):
|
||||
if self.ref:
|
||||
ref = move_ref + ' - ' + self.ref if move_ref else self.ref
|
||||
data = {
|
||||
'statement_line_id': self.id,
|
||||
'journal_id': self.statement_id.journal_id.id,
|
||||
'date': self.date,
|
||||
'ref': ref,
|
||||
@@ -873,9 +870,8 @@ class AccountBankStatementLine(models.Model):
|
||||
# Fully reconciled moves are just linked to the bank statement
|
||||
total = self.amount
|
||||
for aml_rec in payment_aml_rec:
|
||||
total -= aml_rec.debit-aml_rec.credit
|
||||
aml_rec.write({'statement_id': self.statement_id.id})
|
||||
aml_rec.move_id.write({'statement_line_id': self.id})
|
||||
total -= aml_rec.debit - aml_rec.credit
|
||||
aml_rec.write({'statement_line_id': self.id})
|
||||
counterpart_moves = (counterpart_moves | aml_rec.move_id)
|
||||
|
||||
# Create move line(s). Either matching an existing journal entry (eg. invoice), in which
|
||||
@@ -923,7 +919,7 @@ class AccountBankStatementLine(models.Model):
|
||||
for aml_dict in to_create:
|
||||
aml_dict['move_id'] = move.id
|
||||
aml_dict['partner_id'] = self.partner_id.id
|
||||
aml_dict['statement_id'] = self.statement_id.id
|
||||
aml_dict['statement_line_id'] = self.id
|
||||
if st_line_currency.id != company_currency.id:
|
||||
aml_dict['amount_currency'] = aml_dict['debit'] - aml_dict['credit']
|
||||
aml_dict['currency_id'] = st_line_currency.id
|
||||
|
||||
@@ -136,28 +136,20 @@ class account_journal(models.Model):
|
||||
def get_journal_dashboard_datas(self):
|
||||
currency = self.currency_id or self.company_id.currency_id
|
||||
number_to_reconcile = last_balance = account_sum = 0
|
||||
ac_bnk_stmt = []
|
||||
title = ''
|
||||
number_draft = number_waiting = number_late = sum_draft = sum_waiting = sum_late = 0
|
||||
if self.type in ['bank', 'cash']:
|
||||
last_bank_stmt = self.env['account.bank.statement'].search([('journal_id', 'in', self.ids)], order="date desc, id desc", limit=1)
|
||||
last_balance = last_bank_stmt and last_bank_stmt[0].balance_end or 0
|
||||
#Get the number of items to reconcile for that bank journal
|
||||
self.env.cr.execute("""SELECT COUNT(DISTINCT(statement_line_id))
|
||||
FROM account_move where statement_line_id
|
||||
IN (SELECT line.id
|
||||
FROM account_bank_statement_line AS line
|
||||
LEFT JOIN account_bank_statement AS st
|
||||
ON line.statement_id = st.id
|
||||
WHERE st.journal_id IN %s and st.state = 'open')""", (tuple(self.ids),))
|
||||
already_reconciled = self.env.cr.fetchone()[0]
|
||||
self.env.cr.execute("""SELECT COUNT(line.id)
|
||||
FROM account_bank_statement_line AS line
|
||||
LEFT JOIN account_bank_statement AS st
|
||||
ON line.statement_id = st.id
|
||||
WHERE st.journal_id IN %s and st.state = 'open'""", (tuple(self.ids),))
|
||||
all_lines = self.env.cr.fetchone()[0]
|
||||
number_to_reconcile = all_lines - already_reconciled
|
||||
self.env.cr.execute("""SELECT COUNT(DISTINCT(line.id))
|
||||
FROM account_bank_statement_line AS line
|
||||
LEFT JOIN account_bank_statement AS st
|
||||
ON line.statement_id = st.id
|
||||
WHERE st.journal_id IN %s AND st.state = 'open'
|
||||
AND not exists (select 1 from account_move_line aml where aml.statement_line_id = line.id)
|
||||
""", (tuple(self.ids),))
|
||||
number_to_reconcile = self.env.cr.fetchone()[0]
|
||||
# optimization to read sum of balance from account_move_line
|
||||
account_ids = tuple(filter(None, [self.default_debit_account_id.id, self.default_credit_account_id.id]))
|
||||
if account_ids:
|
||||
|
||||
@@ -98,7 +98,6 @@ class AccountMove(models.Model):
|
||||
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', store=True, readonly=True,
|
||||
default=lambda self: self.env.user.company_id)
|
||||
matched_percentage = fields.Float('Percentage Matched', compute='_compute_matched_percentage', digits=0, store=True, readonly=True, help="Technical field used in cash basis method")
|
||||
statement_line_id = fields.Many2one('account.bank.statement.line', index=True, string='Bank statement line reconciled with this entry', copy=False, readonly=True)
|
||||
# Dummy Account field to search on account.move by account_id
|
||||
dummy_account_id = fields.Many2one('account.account', related='line_ids.account_id', string='Account', store=False)
|
||||
tax_cash_basis_rec_id = fields.Many2one(
|
||||
@@ -389,7 +388,8 @@ class AccountMoveLine(models.Model):
|
||||
narration = fields.Text(related='move_id.narration', string='Narration')
|
||||
ref = fields.Char(related='move_id.ref', string='Reference', store=True, copy=False, index=True)
|
||||
payment_id = fields.Many2one('account.payment', string="Originator Payment", help="Payment that created this entry")
|
||||
statement_id = fields.Many2one('account.bank.statement', string='Statement',
|
||||
statement_line_id = fields.Many2one('account.bank.statement.line', index=True, string='Bank statement line reconciled with this entry', copy=False, readonly=True)
|
||||
statement_id = fields.Many2one('account.bank.statement', related='statement_line_id.statement_id', string='Statement', store=True,
|
||||
help="The bank statement used for bank reconciliation", index=True, copy=False)
|
||||
reconciled = fields.Boolean(compute='_amount_residual', store=True)
|
||||
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Matching Number")
|
||||
@@ -1169,7 +1169,7 @@ class AccountMoveLine(models.Model):
|
||||
self.invoice_id.message_post(body=msg) #TODO: check it is an internal note (not a regular email)!
|
||||
#when making a reconciliation on an existing liquidity journal item, mark the payment as reconciled
|
||||
for record in self:
|
||||
if 'statement_id' in vals and record.payment_id:
|
||||
if 'statement_line_id' in vals and record.payment_id:
|
||||
# In case of an internal transfer, there are 2 liquidity move lines to match with a bank statement
|
||||
if all(line.statement_id for line in record.payment_id.move_line_ids.filtered(lambda r: r.id != record.id and r.account_id.internal_type=='liquidity')):
|
||||
record.payment_id.state = 'reconciled'
|
||||
|
||||
@@ -31,10 +31,9 @@ class TestBankStatementReconciliation(AccountingTestCase):
|
||||
}])
|
||||
|
||||
# check everything went as expected
|
||||
rec_move = st_line.journal_entry_ids[0]
|
||||
self.assertTrue(rec_move)
|
||||
self.assertTrue(st_line.journal_entry_ids)
|
||||
counterpart_mv_line = None
|
||||
for l in rec_move.line_ids:
|
||||
for l in st_line.journal_entry_ids:
|
||||
if l.account_id.user_type_id.type == 'receivable':
|
||||
counterpart_mv_line = l
|
||||
break
|
||||
|
||||
@@ -139,7 +139,7 @@ class TestPayment(AccountingTestCase):
|
||||
bank_statement = self.reconcile(liquidity_aml, 200, 0, False)
|
||||
|
||||
self.assertEqual(liquidity_aml.statement_id, bank_statement)
|
||||
self.assertEqual(liquidity_aml.move_id.statement_line_id, bank_statement.line_ids[0])
|
||||
self.assertEqual(liquidity_aml.statement_line_id, bank_statement.line_ids[0])
|
||||
|
||||
self.assertEqual(payment.state, 'reconciled')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user