summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Seinlet <nse@openerp.com>2019-05-09 10:10:32 +0000
committerNicolas Seinlet <nse@openerp.com>2019-05-09 10:10:32 +0000
commitdbaef7de1743c19b21dfe8d89e2ae105362aaf7e (patch)
treee3a9831aa0860cd3b11a65bafe414dba9eeaad75
parentf1d6a6108e40569d4716cf45feb2b30cabf547bd (diff)
[IMP] account: faster reconciliation
avoid to get journal_entry_ids, to avoid pushing those ids in cache and having them read uselessly when accessing an account.move.line recordset. Use search and search_read instead of ORM __get__ closes odoo/odoo#33192 Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com> Co-authored-by: william-andre <wan@odoo.com>
-rw-r--r--addons/account/models/account_bank_statement.py123
-rw-r--r--addons/account/models/account_move.py29
2 files changed, 107 insertions, 45 deletions
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index 58c9825164e..39ede2913fa 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -435,6 +435,7 @@ class AccountBankStatementLine(models.Model):
""" Prepare the dict of values to balance the move.
:param recordset move: the account.move to link the move line
+ :param dict move: a dict of vals of a account.move which will be created later
:param float amount: the amount of transaction that wasn't already reconciled
"""
company_currency = self.journal_id.company_id.currency_id
@@ -442,19 +443,23 @@ class AccountBankStatementLine(models.Model):
st_line_currency = self.currency_id or statement_currency
amount_currency = False
st_line_currency_rate = self.currency_id and (self.amount_currency / self.amount) or False
+ if isinstance(move, dict):
+ amount_sum = sum(x[2].get('amount_currency', 0) for x in move['line_ids'])
+ else:
+ amount_sum = sum(x.amount_currency for x in move.line_ids)
# We have several use case here to compare the currency and amount currency of counterpart line to balance the move:
if st_line_currency != company_currency and st_line_currency == statement_currency:
# company in currency A, statement in currency B and transaction in currency B
# counterpart line must have currency B and correct amount is inverse of already existing lines
- amount_currency = -sum([x.amount_currency for x in move.line_ids])
+ amount_currency = -amount_sum
elif st_line_currency != company_currency and statement_currency == company_currency:
# company in currency A, statement in currency A and transaction in currency B
# counterpart line must have currency B and correct amount is inverse of already existing lines
- amount_currency = -sum([x.amount_currency for x in move.line_ids])
+ amount_currency = -amount_sum
elif st_line_currency != company_currency and st_line_currency != statement_currency:
# company in currency A, statement in currency B and transaction in currency C
# counterpart line must have currency B and use rate between B and C to compute correct amount
- amount_currency = -sum([x.amount_currency for x in move.line_ids])/st_line_currency_rate
+ amount_currency = -amount_sum/st_line_currency_rate
elif st_line_currency == company_currency and statement_currency != company_currency:
# company in currency A, statement in currency B and transaction in currency A
# counterpart line must have currency B and amount is computed using the rate between A and B
@@ -463,9 +468,8 @@ class AccountBankStatementLine(models.Model):
# last case is company in currency A, statement in currency A and transaction in currency A
# and in this case counterpart line does not need any second currency nor amount_currency
- return {
+ aml_dict = {
'name': self.name,
- 'move_id': move.id,
'partner_id': self.partner_id and self.partner_id.id or False,
'account_id': amount >= 0 \
and self.statement_id.journal_id.default_credit_account_id.id \
@@ -476,22 +480,71 @@ class AccountBankStatementLine(models.Model):
'currency_id': statement_currency != company_currency and statement_currency.id or (st_line_currency != company_currency and st_line_currency.id or False),
'amount_currency': amount_currency,
}
+ if isinstance(move, self.env['account.move'].__class__):
+ aml_dict['move_id'] = move.id
+ return aml_dict
+ @api.multi
def fast_counterpart_creation(self):
"""This function is called when confirming a bank statement and will allow to automatically process lines without
going in the bank reconciliation widget. By setting an account_id on bank statement lines, it will create a journal
entry using that account to counterpart the bank account
"""
+ payment_list = []
+ move_list = []
+ account_type_receivable = self.env.ref('account.data_account_type_receivable')
+ already_done_stmt_line_ids = [a['statement_line_id'][0] for a in self.env['account.move.line'].read_group([('statement_line_id', 'in', self.ids)], ['statement_line_id'], ['statement_line_id'])]
+ managed_st_line = []
for st_line in self:
# Technical functionality to automatically reconcile by creating a new move line
- if st_line.account_id and not st_line.journal_entry_ids.ids:
- vals = {
+ if st_line.account_id and not st_line.id in already_done_stmt_line_ids:
+ managed_st_line.append(st_line.id)
+ # Create payment vals
+ total = st_line.amount
+ payment_methods = (total > 0) and st_line.journal_id.inbound_payment_method_ids or st_line.journal_id.outbound_payment_method_ids
+ currency = st_line.journal_id.currency_id or st_line.company_id.currency_id
+ partner_type = 'customer' if st_line.account_id.user_type_id == account_type_receivable else 'supplier'
+ payment_list.append({
+ 'payment_method_id': payment_methods and payment_methods[0].id or False,
+ 'payment_type': total > 0 and 'inbound' or 'outbound',
+ 'partner_id': st_line.partner_id.id,
+ 'partner_type': partner_type,
+ 'journal_id': st_line.statement_id.journal_id.id,
+ 'payment_date': st_line.date,
+ 'state': 'reconciled',
+ 'currency_id': currency.id,
+ 'amount': abs(total),
+ 'communication': st_line._get_communication(payment_methods[0] if payment_methods else False),
+ 'name': st_line.statement_id.name or _("Bank Statement %s") % st_line.date,
+ })
+
+ # Create move and move line vals
+ move_vals = st_line._prepare_reconciliation_move(st_line.statement_id.name)
+ aml_dict = {
'name': st_line.name,
'debit': st_line.amount < 0 and -st_line.amount or 0.0,
'credit': st_line.amount > 0 and st_line.amount or 0.0,
'account_id': st_line.account_id.id,
+ 'partner_id': st_line.partner_id.id,
+ 'statement_line_id': st_line.id,
}
- st_line.process_reconciliation(new_aml_dicts=[vals])
+ st_line._prepare_move_line_for_currency(aml_dict, st_line.date or fields.Date.context_today())
+ move_vals['line_ids'] = [(0, 0, aml_dict)]
+ balance_line = self._prepare_reconciliation_move_line(move_vals, st_line.amount)
+ move_vals['line_ids'].append((0, 0, balance_line))
+ move_list.append(move_vals)
+
+ # Creates
+ payment_ids = self.env['account.payment'].create(payment_list)
+ for payment_id, move_vals in pycompat.izip(payment_ids, move_list):
+ for line in move_vals['line_ids']:
+ line[2]['payment_id'] = payment_id.id
+ move_ids = self.env['account.move'].create(move_list)
+ move_ids.post()
+
+ for move, st_line, payment in pycompat.izip(move_ids, self.browse(managed_st_line), payment_ids):
+ st_line.write({'move_name': move.name})
+ payment.write({'payment_reference': move.name})
def _get_communication(self, payment_method_id):
return self.name or ''
@@ -581,8 +634,6 @@ class AccountBankStatementLine(models.Model):
# Create move line(s). Either matching an existing journal entry (eg. invoice), in which
# case we reconcile the existing and the new move lines together, or being a write-off.
if counterpart_aml_dicts or new_aml_dicts:
- st_line_currency = self.currency_id or statement_currency
- st_line_currency_rate = self.currency_id and (self.amount_currency / self.amount) or False
# Create the move
self.sequence = self.statement_id.line_ids.ids.index(self.id) + 1
@@ -621,32 +672,12 @@ class AccountBankStatementLine(models.Model):
# Complete dicts to create both counterpart move lines and write-offs
to_create = (counterpart_aml_dicts + new_aml_dicts)
- company = self.company_id
date = self.date or fields.Date.today()
for aml_dict in to_create:
aml_dict['move_id'] = move.id
aml_dict['partner_id'] = self.partner_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
- if self.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate:
- # Statement is in company currency but the transaction is in foreign currency
- aml_dict['debit'] = company_currency.round(aml_dict['debit'] / st_line_currency_rate)
- aml_dict['credit'] = company_currency.round(aml_dict['credit'] / st_line_currency_rate)
- elif self.currency_id and st_line_currency_rate:
- # Statement is in foreign currency and the transaction is in another one
- aml_dict['debit'] = statement_currency._convert(aml_dict['debit'] / st_line_currency_rate, company_currency, company, date)
- aml_dict['credit'] = statement_currency._convert(aml_dict['credit'] / st_line_currency_rate, company_currency, company, date)
- else:
- # Statement is in foreign currency and no extra currency is given for the transaction
- aml_dict['debit'] = st_line_currency._convert(aml_dict['debit'], company_currency, company, date)
- aml_dict['credit'] = st_line_currency._convert(aml_dict['credit'], company_currency, company, date)
- elif statement_currency.id != company_currency.id:
- # Statement is in foreign currency but the transaction is in company currency
- prorata_factor = (aml_dict['debit'] - aml_dict['credit']) / self.amount_currency
- aml_dict['amount_currency'] = prorata_factor * self.amount
- aml_dict['currency_id'] = statement_currency.id
+ self._prepare_move_line_for_currency(aml_dict, date)
# Create write-offs
for aml_dict in new_aml_dicts:
@@ -696,6 +727,36 @@ class AccountBankStatementLine(models.Model):
counterpart_moves.assert_balanced()
return counterpart_moves
+ @api.multi
+ def _prepare_move_line_for_currency(self, aml_dict, date):
+ self.ensure_one()
+ company_currency = self.journal_id.company_id.currency_id
+ statement_currency = self.journal_id.currency_id or company_currency
+ st_line_currency = self.currency_id or statement_currency
+ st_line_currency_rate = self.currency_id and (self.amount_currency / self.amount) or False
+ company = self.company_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
+ if self.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate:
+ # Statement is in company currency but the transaction is in foreign currency
+ aml_dict['debit'] = company_currency.round(aml_dict['debit'] / st_line_currency_rate)
+ aml_dict['credit'] = company_currency.round(aml_dict['credit'] / st_line_currency_rate)
+ elif self.currency_id and st_line_currency_rate:
+ # Statement is in foreign currency and the transaction is in another one
+ aml_dict['debit'] = statement_currency._convert(aml_dict['debit'] / st_line_currency_rate, company_currency, company, date)
+ aml_dict['credit'] = statement_currency._convert(aml_dict['credit'] / st_line_currency_rate, company_currency, company, date)
+ else:
+ # Statement is in foreign currency and no extra currency is given for the transaction
+ aml_dict['debit'] = st_line_currency._convert(aml_dict['debit'], company_currency, company, date)
+ aml_dict['credit'] = st_line_currency._convert(aml_dict['credit'], company_currency, company, date)
+ elif statement_currency.id != company_currency.id:
+ # Statement is in foreign currency but the transaction is in company currency
+ prorata_factor = (aml_dict['debit'] - aml_dict['credit']) / self.amount_currency
+ aml_dict['amount_currency'] = prorata_factor * self.amount
+ aml_dict['currency_id'] = statement_currency.id
+
def _check_invoice_state(self, invoice):
if invoice.state == 'in_payment' and all([payment.state == 'reconciled' for payment in invoice.mapped('payment_move_line_ids.payment_id')]):
invoice.write({'state': 'paid'})
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 6d11d14aa8b..62ee5dfc7d6 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -54,12 +54,14 @@ class AccountMove(models.Model):
if line.account_id.user_type_id.type in ('receivable', 'payable'):
amount = abs(line.debit - line.credit)
total_amount += amount
- for partial_line in (line.matched_debit_ids + line.matched_credit_ids):
- total_reconciled += partial_line.amount
precision_currency = move.currency_id or move.company_id.currency_id
if float_is_zero(total_amount, precision_rounding=precision_currency.rounding):
move.matched_percentage = 1.0
else:
+ for line in move.line_ids:
+ if line.account_id.user_type_id.type in ('receivable', 'payable'):
+ for partial_line in (line.matched_debit_ids + line.matched_credit_ids):
+ total_reconciled += partial_line.amount
move.matched_percentage = total_reconciled / total_amount
@api.one
@@ -740,22 +742,21 @@ class AccountMoveLine(models.Model):
In case of full reconciliation, all moves belonging to the reconciliation will belong to the same account_full_reconcile object.
"""
# Get first all aml involved
- part_recs = self.env['account.partial.reconcile'].search(['|', ('debit_move_id', 'in', self.ids), ('credit_move_id', 'in', self.ids)])
- amls = self
- todo = set(part_recs)
+ todo = self.env['account.partial.reconcile'].search_read(['|', ('debit_move_id', 'in', self.ids), ('credit_move_id', 'in', self.ids)], ['debit_move_id', 'credit_move_id'])
+ amls = set(self.ids)
seen = set()
while todo:
- partial_rec = todo.pop()
- seen.add(partial_rec)
- for aml in [partial_rec.debit_move_id, partial_rec.credit_move_id]:
- if aml not in amls:
- amls += aml
- for x in aml.matched_debit_ids | aml.matched_credit_ids:
- if x not in seen:
- todo.add(x)
- partial_rec_ids = [x.id for x in seen]
+ aml_ids = [rec['debit_move_id'][0] for rec in todo if rec['debit_move_id']] + [rec['credit_move_id'][0] for rec in todo if rec['credit_move_id']]
+ amls |= set(aml_ids)
+ seen |= set([rec['id'] for rec in todo])
+ todo = self.env['account.partial.reconcile'].search_read(['&', '|', ('credit_move_id', 'in', aml_ids), ('debit_move_id', 'in', aml_ids), '!', ('id', 'in', list(seen))], ['debit_move_id', 'credit_move_id'])
+
+ partial_rec_ids = list(seen)
if not amls:
return
+ else:
+ amls = self.browse(list(amls))
+
# If we have multiple currency, we can only base ourselve on debit-credit to see if it is fully reconciled
currency = set([a.currency_id for a in amls if a.currency_id.id != False])
multiple_currency = False