mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-24 11:42:02 +02:00
19.0 vanilla
This commit is contained in:
parent
ba20ce7443
commit
768b70e05e
2357 changed files with 1057103 additions and 712486 deletions
|
|
@ -1,16 +1,15 @@
|
|||
from odoo import api, Command, fields, models, _
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import html2plaintext
|
||||
from odoo.tools.misc import str2bool
|
||||
|
||||
from odoo.addons.base.models.res_bank import sanitize_account_number
|
||||
from odoo.fields import Command, Domain
|
||||
|
||||
from xmlrpc.client import MAXINT
|
||||
from itertools import product
|
||||
|
||||
from odoo.tools import SQL
|
||||
from odoo.tools.misc import str2bool
|
||||
|
||||
|
||||
class AccountBankStatementLine(models.Model):
|
||||
_name = "account.bank.statement.line"
|
||||
_name = 'account.bank.statement.line'
|
||||
_inherits = {'account.move': 'move_id'}
|
||||
_description = "Bank Statement Line"
|
||||
_order = "internal_index desc"
|
||||
|
|
@ -23,34 +22,30 @@ class AccountBankStatementLine(models.Model):
|
|||
# - there should be a better way for syncing account_moves with bank transactions, payments, invoices, etc.
|
||||
|
||||
# == Business fields ==
|
||||
def default_get(self, fields_list):
|
||||
defaults = super().default_get(fields_list)
|
||||
# copy the date and statement from the latest transaction of the same journal to help the user
|
||||
# to enter the next transaction, they do not have to enter the date and the statement every time until the
|
||||
# statement is completed. It is only possible if we know the journal that is used, so it can only be done
|
||||
# in a view in which the journal is already set and so is single journal view.
|
||||
if 'journal_id' in defaults and 'date' in fields_list:
|
||||
last_line = self.search([
|
||||
('journal_id', '=', defaults.get('journal_id')),
|
||||
('state', '=', 'posted'),
|
||||
], limit=1)
|
||||
statement = last_line.statement_id
|
||||
if statement:
|
||||
defaults.setdefault('date', statement.date)
|
||||
elif last_line:
|
||||
defaults.setdefault('date', last_line.date)
|
||||
|
||||
return defaults
|
||||
|
||||
move_id = fields.Many2one(
|
||||
comodel_name='account.move',
|
||||
auto_join=True,
|
||||
bypass_search_access=True,
|
||||
string='Journal Entry', required=True, readonly=True, ondelete='cascade',
|
||||
index=True,
|
||||
check_company=True)
|
||||
journal_id = fields.Many2one(
|
||||
comodel_name='account.journal',
|
||||
inherited=True,
|
||||
related='move_id.journal_id', store=True, readonly=False, precompute=True,
|
||||
index=False, # covered by account_bank_statement_line_main_idx
|
||||
required=True,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name='res.company',
|
||||
inherited=True,
|
||||
related='move_id.company_id', store=True, readonly=False, precompute=True,
|
||||
index=False, # covered by account_bank_statement_line_main_idx
|
||||
required=True,
|
||||
)
|
||||
statement_id = fields.Many2one(
|
||||
comodel_name='account.bank.statement',
|
||||
string='Statement',
|
||||
index=True,
|
||||
)
|
||||
|
||||
# Payments generated during the reconciliation of this bank statement lines.
|
||||
|
|
@ -73,11 +68,11 @@ class AccountBankStatementLine(models.Model):
|
|||
|
||||
# This field is used to record the third party name when importing bank statement in electronic format,
|
||||
# when the partner doesn't exist yet in the database (or cannot be found).
|
||||
partner_name = fields.Char()
|
||||
partner_name = fields.Char(index='btree_not_null')
|
||||
|
||||
# Transaction type is used in electronic format, when the type of transaction is available in the imported file.
|
||||
transaction_type = fields.Char()
|
||||
payment_ref = fields.Char(string='Label')
|
||||
payment_ref = fields.Char(string='Label', index='trigram')
|
||||
currency_id = fields.Many2one(
|
||||
comodel_name='res.currency',
|
||||
string='Journal Currency',
|
||||
|
|
@ -129,7 +124,6 @@ class AccountBankStatementLine(models.Model):
|
|||
internal_index = fields.Char(
|
||||
string='Internal Reference',
|
||||
compute='_compute_internal_index', store=True,
|
||||
index=True,
|
||||
)
|
||||
|
||||
# Technical field indicating if the statement line is already reconciled.
|
||||
|
|
@ -143,6 +137,20 @@ class AccountBankStatementLine(models.Model):
|
|||
statement_valid = fields.Boolean(
|
||||
related='statement_id.is_valid',
|
||||
)
|
||||
statement_balance_end_real = fields.Monetary(
|
||||
related='statement_id.balance_end_real',
|
||||
)
|
||||
statement_name = fields.Char(
|
||||
string="Statement Name",
|
||||
related='statement_id.name',
|
||||
)
|
||||
|
||||
# Technical field to store details about the bank statement line
|
||||
transaction_details = fields.Json(readonly=True)
|
||||
|
||||
_unreconciled_idx = models.Index("(journal_id, company_id, internal_index) WHERE is_reconciled IS NOT TRUE")
|
||||
_orphan_idx = models.Index("(journal_id, company_id, internal_index) WHERE statement_id IS NULL")
|
||||
_main_idx = models.Index("(journal_id, company_id, internal_index)")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COMPUTE METHODS
|
||||
|
|
@ -176,10 +184,11 @@ class AccountBankStatementLine(models.Model):
|
|||
# the user can split on that lines, but their balance should be the same as previous posted line
|
||||
# we do the same for the canceled lines, in order to keep using them as anchor points
|
||||
|
||||
self.statement_id.flush_model(['balance_start', 'first_line_index'])
|
||||
self.flush_model(['internal_index', 'date', 'journal_id', 'statement_id', 'amount', 'state'])
|
||||
record_by_id = {x.id: x for x in self}
|
||||
|
||||
company2children = {
|
||||
company: self.env['res.company'].search([('id', 'child_of', company.id)])
|
||||
for company in self.journal_id.company_id
|
||||
}
|
||||
for journal in self.journal_id:
|
||||
journal_lines_indexes = self.filtered(lambda line: line.journal_id == journal)\
|
||||
.sorted('internal_index')\
|
||||
|
|
@ -187,7 +196,8 @@ class AccountBankStatementLine(models.Model):
|
|||
min_index, max_index = journal_lines_indexes[0], journal_lines_indexes[-1]
|
||||
|
||||
# Find the oldest index for each journal.
|
||||
self._cr.execute(
|
||||
self.env['account.bank.statement'].flush_model(['first_line_index', 'journal_id', 'balance_start'])
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
SELECT first_line_index, COALESCE(balance_start, 0.0)
|
||||
FROM account_bank_statement
|
||||
|
|
@ -197,19 +207,20 @@ class AccountBankStatementLine(models.Model):
|
|||
ORDER BY first_line_index DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
[min_index, journal.id],
|
||||
[min_index or '', journal.id],
|
||||
)
|
||||
current_running_balance = 0.0
|
||||
extra_clause = ''
|
||||
extra_params = []
|
||||
row = self._cr.fetchone()
|
||||
extra_clause = SQL()
|
||||
row = self.env.cr.fetchone()
|
||||
if row:
|
||||
starting_index, current_running_balance = row
|
||||
extra_clause = "AND st_line.internal_index >= %s"
|
||||
extra_params.append(starting_index)
|
||||
extra_clause = SQL("AND st_line.internal_index >= %s", starting_index)
|
||||
|
||||
self._cr.execute(
|
||||
f"""
|
||||
self.flush_model(['amount', 'move_id', 'statement_id', 'journal_id', 'internal_index'])
|
||||
self.env['account.bank.statement'].flush_model(['first_line_index', 'balance_start'])
|
||||
self.env['account.move'].flush_model(['state'])
|
||||
self.env.cr.execute(SQL(
|
||||
"""
|
||||
SELECT
|
||||
st_line.id,
|
||||
st_line.amount,
|
||||
|
|
@ -221,14 +232,18 @@ class AccountBankStatementLine(models.Model):
|
|||
LEFT JOIN account_bank_statement st ON st.id = st_line.statement_id
|
||||
WHERE
|
||||
st_line.internal_index <= %s
|
||||
AND move.journal_id = %s
|
||||
{extra_clause}
|
||||
AND st_line.journal_id = %s
|
||||
AND st_line.company_id = ANY(%s)
|
||||
%s
|
||||
ORDER BY st_line.internal_index
|
||||
""",
|
||||
[max_index, journal.id] + extra_params,
|
||||
)
|
||||
max_index or '',
|
||||
journal.id,
|
||||
company2children[journal.company_id].ids,
|
||||
extra_clause,
|
||||
))
|
||||
pending_items = self
|
||||
for st_line_id, amount, is_anchor, balance_start, state in self._cr.fetchall():
|
||||
for st_line_id, amount, is_anchor, balance_start, state in self.env.cr.fetchall():
|
||||
if is_anchor:
|
||||
current_running_balance = balance_start
|
||||
if state == 'posted':
|
||||
|
|
@ -265,7 +280,7 @@ class AccountBankStatementLine(models.Model):
|
|||
f'{st_line._origin.id:0>10}'
|
||||
|
||||
@api.depends('journal_id', 'currency_id', 'amount', 'foreign_currency_id', 'amount_currency',
|
||||
'move_id.to_check',
|
||||
'move_id.checked',
|
||||
'move_id.line_ids.account_id', 'move_id.line_ids.amount_currency',
|
||||
'move_id.line_ids.amount_residual_currency', 'move_id.line_ids.currency_id',
|
||||
'move_id.line_ids.matched_debit_ids', 'move_id.line_ids.matched_credit_ids')
|
||||
|
|
@ -278,7 +293,7 @@ class AccountBankStatementLine(models.Model):
|
|||
_liquidity_lines, suspense_lines, _other_lines = st_line._seek_for_lines()
|
||||
|
||||
# Compute residual amount
|
||||
if st_line.to_check:
|
||||
if not st_line.checked:
|
||||
st_line.amount_residual = -st_line.amount_currency if st_line.foreign_currency_id else -st_line.amount
|
||||
elif suspense_lines.account_id.reconcile:
|
||||
st_line.amount_residual = sum(suspense_lines.mapped('amount_residual_currency'))
|
||||
|
|
@ -298,7 +313,6 @@ class AccountBankStatementLine(models.Model):
|
|||
# The journal entry seems reconciled.
|
||||
st_line.is_reconciled = True
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# CONSTRAINT METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
@ -322,11 +336,32 @@ class AccountBankStatementLine(models.Model):
|
|||
# LOW-LEVEL METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
self_ctx = self.with_context(is_statement_line=True)
|
||||
defaults = super(AccountBankStatementLine, self_ctx).default_get(fields)
|
||||
if 'journal_id' in fields and not defaults.get('journal_id'):
|
||||
defaults['journal_id'] = self_ctx.env['account.move']._search_default_journal().id
|
||||
|
||||
if 'date' in fields and not defaults.get('date') and 'journal_id' in defaults:
|
||||
# copy the date and statement from the latest transaction of the same journal to help the user
|
||||
# to enter the next transaction, they do not have to enter the date and the statement every time until the
|
||||
# statement is completed. It is only possible if we know the journal that is used, so it can only be done
|
||||
# in a view in which the journal is already set and so is single journal view.
|
||||
last_line = self.search([
|
||||
('journal_id', '=', defaults['journal_id']),
|
||||
('state', '=', 'posted'),
|
||||
], limit=1)
|
||||
statement = last_line.statement_id
|
||||
if statement:
|
||||
defaults.setdefault('date', statement.date)
|
||||
elif last_line:
|
||||
defaults.setdefault('date', last_line.date)
|
||||
return defaults
|
||||
|
||||
@api.model
|
||||
def new(self, values=None, origin=None, ref=None):
|
||||
st_line = super().new(values, origin, ref)
|
||||
if not st_line.journal_id: # might not be computed because declared by inheritance
|
||||
st_line.move_id._compute_journal_id()
|
||||
return st_line
|
||||
return super(AccountBankStatementLine, self.with_context(is_statement_line=True)).new(values, origin, ref)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
|
|
@ -361,54 +396,61 @@ class AccountBankStatementLine(models.Model):
|
|||
if 'amount' not in vals:
|
||||
vals['amount'] = 0
|
||||
|
||||
st_lines = super().create(vals_list)
|
||||
|
||||
for i, st_line in enumerate(st_lines):
|
||||
counterpart_account_id = counterpart_account_ids[i]
|
||||
|
||||
to_write = {'statement_line_id': st_line.id, 'narration': st_line.narration}
|
||||
st_lines = super(AccountBankStatementLine, self.with_context(is_statement_line=True)).create([{
|
||||
'name': False,
|
||||
**vals,
|
||||
} for vals in vals_list])
|
||||
to_create_lines_vals = []
|
||||
for i, (st_line, vals) in enumerate(zip(st_lines, vals_list)):
|
||||
if 'line_ids' not in vals_list[i]:
|
||||
to_write['line_ids'] = [(0, 0, line_vals) for line_vals in st_line._prepare_move_line_default_vals(
|
||||
counterpart_account_id=counterpart_account_id)]
|
||||
to_create_lines_vals.extend(
|
||||
line_vals
|
||||
for line_vals in st_line._prepare_move_line_default_vals(counterpart_account_ids[i])
|
||||
)
|
||||
to_write = {'statement_line_id': st_line.id, 'narration': st_line.narration, 'name': False}
|
||||
with self.env.protecting(self.env['account.move']._get_protected_vals(vals, st_line)):
|
||||
st_line.move_id.write(to_write)
|
||||
self.env['account.move.line'].create(to_create_lines_vals)
|
||||
self.env.add_to_compute(self.env['account.move']._fields['name'], st_lines.move_id)
|
||||
|
||||
st_line.move_id.write(to_write)
|
||||
|
||||
# Otherwise field narration will be recomputed silently (at next flush) when writing on partner_id
|
||||
self.env.remove_to_compute(st_line.move_id._fields['narration'], st_line.move_id)
|
||||
# Otherwise field narration will be recomputed silently (at next flush) when writing on partner_id
|
||||
self.env.remove_to_compute(self.env['account.move']._fields['narration'], st_lines.move_id)
|
||||
|
||||
# No need for the user to manage their status (from 'Draft' to 'Posted')
|
||||
st_lines.move_id.action_post()
|
||||
return st_lines
|
||||
return st_lines.with_env(self.env) # clear the context
|
||||
|
||||
def write(self, vals):
|
||||
# OVERRIDE
|
||||
|
||||
res = super().write(vals)
|
||||
res = super(AccountBankStatementLine, self.with_context(skip_readonly_check=True)).write(vals)
|
||||
self._synchronize_to_moves(set(vals.keys()))
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
# OVERRIDE to unlink the inherited account.move (move_id field) as well.
|
||||
moves = self.with_context(force_delete=True).mapped('move_id')
|
||||
tracked_lines = self.filtered(lambda stl: stl.company_id.restrictive_audit_trail)
|
||||
tracked_lines.move_id.button_cancel()
|
||||
moves_to_delete = (self - tracked_lines).move_id
|
||||
res = super().unlink()
|
||||
moves.unlink()
|
||||
moves_to_delete.with_context(force_delete=True).unlink()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||||
# Add latest running_balance in the read_group
|
||||
result = super(AccountBankStatementLine, self).read_group(
|
||||
domain, fields, groupby, offset=offset,
|
||||
limit=limit, orderby=orderby, lazy=lazy)
|
||||
def formatted_read_group(self, domain, groupby=(), aggregates=(), having=(), offset=0, limit=None, order=None) -> list[dict]:
|
||||
# Add latest running_balance in the formatted_read_group
|
||||
result = super().formatted_read_group(
|
||||
domain, groupby, aggregates, having=having,
|
||||
offset=offset, limit=limit, order=order)
|
||||
show_running_balance = False
|
||||
# We loop over the content of groupby because the groupby date is in the form of "date:granularity"
|
||||
for el in groupby:
|
||||
if (el == 'statement_id' or el == 'journal_id' or el.startswith('date')) and 'running_balance' in fields:
|
||||
if (el == 'statement_id' or el == 'journal_id' or el.startswith('date')) and self.env.context.get('show_running_balance_latest'):
|
||||
show_running_balance = True
|
||||
break
|
||||
if show_running_balance:
|
||||
for group_line in result:
|
||||
group_line['running_balance'] = self.search(group_line.get('__domain'), limit=1).running_balance or 0.0
|
||||
group_line['running_balance'] = self.search(group_line['__extra_domain'] + domain, limit=1).running_balance or 0.0
|
||||
return result
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
@ -423,8 +465,8 @@ class AccountBankStatementLine(models.Model):
|
|||
self.payment_ids.unlink()
|
||||
|
||||
for st_line in self:
|
||||
st_line.with_context(force_delete=True).write({
|
||||
'to_check': False,
|
||||
st_line.with_context(force_delete=True, skip_readonly_check=True).write({
|
||||
'checked': True,
|
||||
'line_ids': [Command.clear()] + [
|
||||
Command.create(line_vals) for line_vals in st_line._prepare_move_line_default_vals()],
|
||||
})
|
||||
|
|
@ -433,61 +475,48 @@ class AccountBankStatementLine(models.Model):
|
|||
# HELPERS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _check_allow_unlink(self):
|
||||
if self.statement_id.filtered(lambda stmt: stmt.is_valid and stmt.is_complete):
|
||||
raise UserError(_("You can not delete a transaction from a valid statement.\n"
|
||||
"If you want to delete it, please remove the statement first."))
|
||||
|
||||
def _find_or_create_bank_account(self):
|
||||
self.ensure_one()
|
||||
if not self.partner_id:
|
||||
return self.env['res.partner.bank']
|
||||
if str2bool(self.env['ir.config_parameter'].sudo().get_param("account.skip_create_bank_account_on_reconcile")):
|
||||
return self.env['res.partner.bank'].search([
|
||||
('acc_number', '=', self.account_number),
|
||||
('partner_id', '=', self.partner_id.id),
|
||||
('company_id', 'in', [False, self.company_id.id]),
|
||||
], limit=1)
|
||||
return self.env['res.partner.bank']._find_or_create_bank_account(
|
||||
account_number=self.account_number,
|
||||
partner=self.partner_id,
|
||||
company=self.company_id,
|
||||
)
|
||||
|
||||
# There is a sql constraint on res.partner.bank ensuring an unique pair <partner, account number>.
|
||||
# Since it's not dependent of the company, we need to search on others company too to avoid the creation
|
||||
# of an extra res.partner.bank raising an error coming from this constraint.
|
||||
# However, at the end, we need to filter out the results to not trigger the check_company when trying to
|
||||
# assign a res.partner.bank owned by another company.
|
||||
bank_account = self.env['res.partner.bank'].sudo().with_context(active_test=False).search([
|
||||
('acc_number', '=', self.account_number),
|
||||
('partner_id', '=', self.partner_id.id),
|
||||
])
|
||||
if not bank_account and not str2bool(
|
||||
self.env['ir.config_parameter'].sudo().get_param("account.skip_create_bank_account_on_reconcile")
|
||||
):
|
||||
bank_account = self.env['res.partner.bank'].create({
|
||||
'acc_number': self.account_number,
|
||||
'partner_id': self.partner_id.id,
|
||||
'journal_id': None,
|
||||
})
|
||||
return bank_account.filtered(lambda x: x.company_id.id in (False, self.company_id.id))
|
||||
|
||||
def _get_amounts_with_currencies(self):
|
||||
"""
|
||||
Returns the line amount in company, journal and foreign currencies
|
||||
"""
|
||||
def _get_default_amls_matching_domain(self, allow_draft=False):
|
||||
self.ensure_one()
|
||||
|
||||
company_currency = self.journal_id.company_id.currency_id
|
||||
journal_currency = self.journal_id.currency_id or company_currency
|
||||
foreign_currency = self.foreign_currency_id or journal_currency or company_currency
|
||||
|
||||
journal_amount = self.amount
|
||||
if foreign_currency == journal_currency:
|
||||
transaction_amount = journal_amount
|
||||
else:
|
||||
transaction_amount = self.amount_currency
|
||||
if journal_currency == company_currency:
|
||||
company_amount = journal_amount
|
||||
elif foreign_currency == company_currency:
|
||||
company_amount = transaction_amount
|
||||
else:
|
||||
company_amount = journal_currency._convert(journal_amount, company_currency,
|
||||
self.journal_id.company_id, self.date)
|
||||
return company_amount, company_currency, journal_amount, journal_currency, transaction_amount, foreign_currency
|
||||
|
||||
def _get_default_amls_matching_domain(self):
|
||||
return [
|
||||
all_reconcilable_account_ids = self.env['account.account'].sudo().search([
|
||||
("company_ids", "child_of", self.company_id.root_id.id),
|
||||
('reconcile', '=', True),
|
||||
]).ids
|
||||
state_domain = [('parent_state', '=', 'posted')]
|
||||
if allow_draft:
|
||||
# Set if bank recon will display draft invoices/bills that have a partner.
|
||||
# Usually not applied when used by bank recon models (no suggestions & auto matching for draft entries)
|
||||
partnered_drafts_domain = [('parent_state', '=', 'draft'), ('partner_id', '!=', False)]
|
||||
state_domain = Domain.OR([state_domain, partnered_drafts_domain])
|
||||
return state_domain + [
|
||||
# Base domain.
|
||||
('display_type', 'not in', ('line_section', 'line_note')),
|
||||
('parent_state', '=', 'posted'),
|
||||
('company_id', '=', self.company_id.id),
|
||||
('display_type', 'not in', ('line_section', 'line_subsection', 'line_note')),
|
||||
('company_id', 'in', self.env['res.company'].search([('id', 'child_of', self.company_id.id)]).ids), # allow to match invoices from same or children companies to be consistant with what's shown in the interface
|
||||
# Reconciliation domain.
|
||||
('reconciled', '=', False),
|
||||
('account_id.reconcile', '=', True),
|
||||
# Domain to use the account_move_line__unreconciled_index
|
||||
('account_id', 'in', all_reconcilable_account_ids),
|
||||
# Special domain for payments.
|
||||
'|',
|
||||
('account_id.account_type', 'not in', ('asset_receivable', 'liability_payable')),
|
||||
|
|
@ -500,30 +529,21 @@ class AccountBankStatementLine(models.Model):
|
|||
def _get_default_journal(self):
|
||||
journal_type = self.env.context.get('journal_type', 'bank')
|
||||
return self.env['account.journal'].search([
|
||||
*self.env['account.journal']._check_company_domain(self.env.company),
|
||||
('type', '=', journal_type),
|
||||
('company_id', '=', self.env.company.id)
|
||||
], limit=1)
|
||||
|
||||
def _get_st_line_strings_for_matching(self, allowed_fields=None):
|
||||
""" Collect the strings that could be used on the statement line to perform some matching.
|
||||
|
||||
:param allowed_fields: A explicit list of fields to consider.
|
||||
:return: A list of strings.
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
st_line_text_values = []
|
||||
if not allowed_fields or 'payment_ref' in allowed_fields:
|
||||
if self.payment_ref:
|
||||
st_line_text_values.append(self.payment_ref)
|
||||
if not allowed_fields or 'narration' in allowed_fields:
|
||||
value = html2plaintext(self.narration or "")
|
||||
if value:
|
||||
st_line_text_values.append(value)
|
||||
if not allowed_fields or 'ref' in allowed_fields:
|
||||
if self.ref:
|
||||
st_line_text_values.append(self.ref)
|
||||
return st_line_text_values
|
||||
@api.model
|
||||
def _get_default_statement(self, journal_id=None, date=None):
|
||||
statement = self.search(
|
||||
domain=[
|
||||
('journal_id', '=', journal_id or self._get_default_journal().id),
|
||||
('date', '<=', date or fields.Date.today()),
|
||||
],
|
||||
limit=1
|
||||
).statement_id
|
||||
if not statement.is_complete:
|
||||
return statement
|
||||
|
||||
def _get_accounting_amounts_and_currencies(self):
|
||||
""" Retrieve the transaction amount, journal amount and the company amount with their corresponding currencies
|
||||
|
|
@ -618,8 +638,22 @@ class AccountBankStatementLine(models.Model):
|
|||
self.journal_id.display_name,
|
||||
))
|
||||
|
||||
company_amount, _company_currency, journal_amount, journal_currency, transaction_amount, foreign_currency \
|
||||
= self._get_amounts_with_currencies()
|
||||
company_currency = self.journal_id.company_id.sudo().currency_id
|
||||
journal_currency = self.journal_id.currency_id or company_currency
|
||||
foreign_currency = self.foreign_currency_id or journal_currency or company_currency
|
||||
|
||||
journal_amount = self.amount
|
||||
if foreign_currency == journal_currency:
|
||||
transaction_amount = journal_amount
|
||||
else:
|
||||
transaction_amount = self.amount_currency
|
||||
if journal_currency == company_currency:
|
||||
company_amount = journal_amount
|
||||
elif foreign_currency == company_currency:
|
||||
company_amount = transaction_amount
|
||||
else:
|
||||
company_amount = journal_currency\
|
||||
._convert(journal_amount, company_currency, self.journal_id.company_id, self.date)
|
||||
|
||||
liquidity_line_vals = {
|
||||
'name': self.payment_ref,
|
||||
|
|
@ -645,53 +679,6 @@ class AccountBankStatementLine(models.Model):
|
|||
}
|
||||
return [liquidity_line_vals, counterpart_line_vals]
|
||||
|
||||
def _retrieve_partner(self):
|
||||
self.ensure_one()
|
||||
|
||||
# Retrieve the partner from the statement line.
|
||||
if self.partner_id:
|
||||
return self.partner_id
|
||||
|
||||
# Retrieve the partner from the bank account.
|
||||
if self.account_number:
|
||||
account_number_nums = sanitize_account_number(self.account_number)
|
||||
if account_number_nums:
|
||||
domain = [('sanitized_acc_number', 'ilike', account_number_nums)]
|
||||
for extra_domain in ([('company_id', '=', self.company_id.id)], [('company_id', '=', False)]):
|
||||
bank_accounts = self.env['res.partner.bank'].search(extra_domain + domain)
|
||||
if len(bank_accounts.partner_id) == 1:
|
||||
return bank_accounts.partner_id
|
||||
|
||||
# Retrieve the partner from the partner name.
|
||||
if self.partner_name:
|
||||
domains = product(
|
||||
[
|
||||
('name', '=ilike', self.partner_name),
|
||||
('name', 'ilike', self.partner_name),
|
||||
],
|
||||
[
|
||||
('company_id', '=', self.company_id.id),
|
||||
('company_id', '=', False),
|
||||
],
|
||||
)
|
||||
for domain in domains:
|
||||
partner = self.env['res.partner'].search(list(domain) + [('parent_id', '=', False)], limit=2)
|
||||
# Return the partner if there is only one with this name
|
||||
if len(partner) == 1:
|
||||
return partner
|
||||
|
||||
# Retrieve the partner from the 'reconcile models'.
|
||||
rec_models = self.env['account.reconcile.model'].search([
|
||||
('rule_type', '!=', 'writeoff_button'),
|
||||
('company_id', '=', self.company_id.id),
|
||||
])
|
||||
for rec_model in rec_models:
|
||||
partner = rec_model._get_partner_from_mapping(self)
|
||||
if partner and rec_model._is_applicable_for(self, partner):
|
||||
return partner
|
||||
|
||||
return self.env['res.partner']
|
||||
|
||||
def _seek_for_lines(self):
|
||||
""" Helper used to dispatch the journal items between:
|
||||
- The lines using the liquidity account.
|
||||
|
|
@ -723,7 +710,7 @@ class AccountBankStatementLine(models.Model):
|
|||
Also, check both models are still consistent.
|
||||
:param changed_fields: A set containing all modified fields on account.move.
|
||||
"""
|
||||
if self._context.get('skip_account_move_synchronization'):
|
||||
if self.env.context.get('skip_account_move_synchronization'):
|
||||
return
|
||||
|
||||
for st_line in self.with_context(skip_account_move_synchronization=True):
|
||||
|
|
@ -732,7 +719,7 @@ class AccountBankStatementLine(models.Model):
|
|||
st_line_vals_to_write = {}
|
||||
|
||||
if 'line_ids' in changed_fields:
|
||||
liquidity_lines, suspense_lines, _other_lines = st_line._seek_for_lines()
|
||||
liquidity_lines, suspense_lines, other_lines = st_line._seek_for_lines()
|
||||
company_currency = st_line.journal_id.company_id.currency_id
|
||||
journal_currency = st_line.journal_id.currency_id if st_line.journal_id.currency_id != company_currency\
|
||||
else False
|
||||
|
|
@ -741,8 +728,8 @@ class AccountBankStatementLine(models.Model):
|
|||
raise UserError(_(
|
||||
"The journal entry %s reached an invalid state regarding its related statement line.\n"
|
||||
"To be consistent, the journal entry must always have exactly one journal item involving the "
|
||||
"bank/cash account."
|
||||
) % st_line.move_id.display_name)
|
||||
"bank/cash account.",
|
||||
st_line.move_id.display_name))
|
||||
|
||||
st_line_vals_to_write.update({
|
||||
'payment_ref': liquidity_lines.name,
|
||||
|
|
@ -762,8 +749,9 @@ class AccountBankStatementLine(models.Model):
|
|||
|
||||
if len(suspense_lines) > 1:
|
||||
raise UserError(_(
|
||||
"%s reached an invalid state regarding its related statement line.\n"
|
||||
"To be consistent, the journal entry must always have exactly one suspense line.", st_line.move_id.display_name
|
||||
"%(move)s reached an invalid state regarding its related statement line.\n"
|
||||
"To be consistent, the journal entry must always have exactly one suspense line.",
|
||||
move=st_line.move_id.display_name,
|
||||
))
|
||||
elif len(suspense_lines) == 1:
|
||||
if journal_currency and suspense_lines.currency_id == journal_currency:
|
||||
|
|
@ -785,7 +773,7 @@ class AccountBankStatementLine(models.Model):
|
|||
'foreign_currency_id': False,
|
||||
})
|
||||
|
||||
else:
|
||||
elif not other_lines:
|
||||
|
||||
# Update the statement line regarding the foreign currency of the suspense line.
|
||||
|
||||
|
|
@ -799,14 +787,14 @@ class AccountBankStatementLine(models.Model):
|
|||
'currency_id': (st_line.foreign_currency_id or journal_currency or company_currency).id,
|
||||
})
|
||||
|
||||
move.write(move._cleanup_write_orm_values(move, move_vals_to_write))
|
||||
move.with_context(skip_readonly_check=True).write(move._cleanup_write_orm_values(move, move_vals_to_write))
|
||||
st_line.write(move._cleanup_write_orm_values(st_line, st_line_vals_to_write))
|
||||
|
||||
def _synchronize_to_moves(self, changed_fields):
|
||||
""" Update the account.move regarding the modified account.bank.statement.line.
|
||||
:param changed_fields: A list containing all modified fields on account.bank.statement.line.
|
||||
"""
|
||||
if self._context.get('skip_account_move_synchronization'):
|
||||
if self.env.context.get('skip_account_move_synchronization'):
|
||||
return
|
||||
|
||||
if not any(field_name in changed_fields for field_name in (
|
||||
|
|
@ -818,7 +806,8 @@ class AccountBankStatementLine(models.Model):
|
|||
for st_line in self.with_context(skip_account_move_synchronization=True):
|
||||
liquidity_lines, suspense_lines, other_lines = st_line._seek_for_lines()
|
||||
journal = st_line.journal_id
|
||||
company_currency = journal.company_id.currency_id
|
||||
# bypassing access rights restrictions for branch-specific users in a branch company environment.
|
||||
company_currency = journal.company_id.sudo().currency_id
|
||||
journal_currency = journal.currency_id if journal.currency_id != company_currency else False
|
||||
|
||||
line_vals_list = st_line._prepare_move_line_default_vals()
|
||||
|
|
@ -840,13 +829,14 @@ class AccountBankStatementLine(models.Model):
|
|||
st_line_vals['journal_id'] = journal.id
|
||||
if st_line.move_id.partner_id != st_line.partner_id:
|
||||
st_line_vals['partner_id'] = st_line.partner_id.id
|
||||
st_line.move_id.write(st_line_vals)
|
||||
st_line.move_id.with_context(skip_readonly_check=True).write(st_line_vals)
|
||||
|
||||
|
||||
# For optimization purpose, creating the reverse relation of m2o in _inherits saves
|
||||
|
||||
|
||||
# a lot of SQL queries
|
||||
class AccountMove(models.Model):
|
||||
_name = "account.move"
|
||||
_inherit = ['account.move']
|
||||
_inherit = 'account.move'
|
||||
|
||||
statement_line_ids = fields.One2many('account.bank.statement.line', 'move_id', string='Statements')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue