[IMP] account_reconcile_oca: Fix multi currency computation
parent
41549a63e8
commit
0b69f38cac
|
@ -151,11 +151,11 @@ class AccountAccountReconcile(models.Model):
|
||||||
counterparts = data["counterparts"]
|
counterparts = data["counterparts"]
|
||||||
amount = 0.0
|
amount = 0.0
|
||||||
for line_id in counterparts:
|
for line_id in counterparts:
|
||||||
line = self._get_reconcile_line(
|
lines = self._get_reconcile_line(
|
||||||
self.env["account.move.line"].browse(line_id), "other", True, amount
|
self.env["account.move.line"].browse(line_id), "other", True, amount
|
||||||
)
|
)
|
||||||
new_data["data"].append(line)
|
new_data["data"] += lines
|
||||||
amount += line["amount"]
|
amount += sum(line["amount"] for line in lines)
|
||||||
return new_data
|
return new_data
|
||||||
|
|
||||||
def clean_reconcile(self):
|
def clean_reconcile(self):
|
||||||
|
|
|
@ -163,7 +163,7 @@ class AccountBankStatementLine(models.Model):
|
||||||
self.manual_model_id,
|
self.manual_model_id,
|
||||||
self.reconcile_data_info["reconcile_auxiliary_id"],
|
self.reconcile_data_info["reconcile_auxiliary_id"],
|
||||||
),
|
),
|
||||||
self.manual_reference
|
self.manual_reference,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Refreshing data
|
# Refreshing data
|
||||||
|
@ -189,26 +189,19 @@ class AccountBankStatementLine(models.Model):
|
||||||
else:
|
else:
|
||||||
new_data.append(line)
|
new_data.append(line)
|
||||||
if is_new_line:
|
if is_new_line:
|
||||||
new_data.append(
|
reconcile_auxiliary_id, lines = self._get_reconcile_line(
|
||||||
self._get_reconcile_line(
|
|
||||||
self.add_account_move_line_id, "other", True, pending_amount
|
self.add_account_move_line_id, "other", True, pending_amount
|
||||||
)
|
)
|
||||||
)
|
new_data += lines
|
||||||
self.reconcile_data_info = self._recompute_suspense_line(
|
self.reconcile_data_info = self._recompute_suspense_line(
|
||||||
new_data,
|
new_data,
|
||||||
self.reconcile_data_info["reconcile_auxiliary_id"],
|
self.reconcile_data_info["reconcile_auxiliary_id"],
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True,
|
|
||||||
)
|
)
|
||||||
self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False)
|
self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False)
|
||||||
self.add_account_move_line_id = False
|
self.add_account_move_line_id = False
|
||||||
|
|
||||||
def _recompute_suspense_line(
|
def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_reference):
|
||||||
self, data, reconcile_auxiliary_id, manual_reference, exchange_recompute=False
|
|
||||||
):
|
|
||||||
reconcile_auxiliary_id = self._compute_exchange_rate(
|
|
||||||
data, reconcile_auxiliary_id, exchange_recompute=exchange_recompute
|
|
||||||
)
|
|
||||||
can_reconcile = True
|
can_reconcile = True
|
||||||
total_amount = 0
|
total_amount = 0
|
||||||
new_data = []
|
new_data = []
|
||||||
|
@ -450,49 +443,15 @@ class AccountBankStatementLine(models.Model):
|
||||||
new_data.append(new_line)
|
new_data.append(new_line)
|
||||||
return new_data, reconcile_auxiliary_id
|
return new_data, reconcile_auxiliary_id
|
||||||
|
|
||||||
def _compute_exchange_rate(
|
|
||||||
self, data, reconcile_auxiliary_id, exchange_recompute=False
|
|
||||||
):
|
|
||||||
if not exchange_recompute:
|
|
||||||
return reconcile_auxiliary_id
|
|
||||||
foreign_currency = (
|
|
||||||
self.currency_id != self.company_id.currency_id
|
|
||||||
or self.foreign_currency_id
|
|
||||||
or any(line["currency_id"] != line["line_currency_id"] for line in data)
|
|
||||||
)
|
|
||||||
if not foreign_currency or self.is_reconciled:
|
|
||||||
return reconcile_auxiliary_id
|
|
||||||
currency = self.journal_id.currency_id or self.company_id.currency_id
|
|
||||||
amount = sum(d.get("net_amount", 0) for d in data)
|
|
||||||
if not currency.is_zero(amount):
|
|
||||||
account = self.company_id.expense_currency_exchange_account_id
|
|
||||||
if amount > 0:
|
|
||||||
account = self.company_id.income_currency_exchange_account_id
|
|
||||||
data.append(
|
|
||||||
{
|
|
||||||
"reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id,
|
|
||||||
"id": False,
|
|
||||||
"account_id": account.name_get()[0],
|
|
||||||
"partner_id": False,
|
|
||||||
"date": fields.Date.to_string(self.date),
|
|
||||||
"name": self.payment_ref or self.name,
|
|
||||||
"amount": -amount,
|
|
||||||
"net_amount": -amount,
|
|
||||||
"credit": amount if amount > 0 else 0.0,
|
|
||||||
"debit": -amount if amount < 0 else 0.0,
|
|
||||||
"kind": "other",
|
|
||||||
"currency_id": self.currency_id.id,
|
|
||||||
"line_currency_id": self.currency_id.id,
|
|
||||||
"currency_amount": -amount,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
reconcile_auxiliary_id += 1
|
|
||||||
return reconcile_auxiliary_id
|
|
||||||
|
|
||||||
def _default_reconcile_data(self, from_unreconcile=False):
|
def _default_reconcile_data(self, from_unreconcile=False):
|
||||||
liquidity_lines, suspense_lines, other_lines = self._seek_for_lines()
|
liquidity_lines, suspense_lines, other_lines = self._seek_for_lines()
|
||||||
data = [self._get_reconcile_line(line, "liquidity") for line in liquidity_lines]
|
data = []
|
||||||
reconcile_auxiliary_id = 1
|
reconcile_auxiliary_id = 1
|
||||||
|
for line in liquidity_lines:
|
||||||
|
reconcile_auxiliary_id, lines = self._get_reconcile_line(
|
||||||
|
line, "liquidity", reconcile_auxiliary_id=reconcile_auxiliary_id
|
||||||
|
)
|
||||||
|
data += lines
|
||||||
if not from_unreconcile:
|
if not from_unreconcile:
|
||||||
res = (
|
res = (
|
||||||
self.env["account.reconcile.model"]
|
self.env["account.reconcile.model"]
|
||||||
|
@ -507,30 +466,31 @@ class AccountBankStatementLine(models.Model):
|
||||||
data, res["model"], reconcile_auxiliary_id
|
data, res["model"], reconcile_auxiliary_id
|
||||||
),
|
),
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True
|
|
||||||
)
|
)
|
||||||
elif res and res.get("amls"):
|
elif res and res.get("amls"):
|
||||||
amount = self.amount_total_signed
|
amount = self.amount_total_signed
|
||||||
for line in res.get("amls", []):
|
for line in res.get("amls", []):
|
||||||
line_data = self._get_reconcile_line(
|
reconcile_auxiliary_id, line_data = self._get_reconcile_line(
|
||||||
line, "other", is_counterpart=True, max_amount=amount
|
line,
|
||||||
|
"other",
|
||||||
|
is_counterpart=True,
|
||||||
|
max_amount=amount,
|
||||||
|
reconcile_auxiliary_id=reconcile_auxiliary_id,
|
||||||
)
|
)
|
||||||
amount -= line_data.get("amount")
|
amount -= sum(line.get("amount") for line in line_data)
|
||||||
data.append(line_data)
|
data += line_data
|
||||||
return self._recompute_suspense_line(
|
return self._recompute_suspense_line(
|
||||||
data,
|
data,
|
||||||
reconcile_auxiliary_id,
|
reconcile_auxiliary_id,
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True,
|
|
||||||
)
|
)
|
||||||
return self._recompute_suspense_line(
|
for line in other_lines:
|
||||||
data
|
reconcile_auxiliary_id, lines = self._get_reconcile_line(
|
||||||
+ [
|
|
||||||
self._get_reconcile_line(
|
|
||||||
line, "other", from_unreconcile=from_unreconcile
|
line, "other", from_unreconcile=from_unreconcile
|
||||||
)
|
)
|
||||||
for line in other_lines
|
data += lines
|
||||||
],
|
return self._recompute_suspense_line(
|
||||||
|
data,
|
||||||
reconcile_auxiliary_id,
|
reconcile_auxiliary_id,
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
)
|
)
|
||||||
|
@ -699,10 +659,12 @@ class AccountBankStatementLine(models.Model):
|
||||||
if not res:
|
if not res:
|
||||||
continue
|
continue
|
||||||
liquidity_lines, suspense_lines, other_lines = record._seek_for_lines()
|
liquidity_lines, suspense_lines, other_lines = record._seek_for_lines()
|
||||||
data = [
|
data = []
|
||||||
record._get_reconcile_line(line, "liquidity")
|
for line in liquidity_lines:
|
||||||
for line in liquidity_lines
|
reconcile_auxiliary_id, lines = record._get_reconcile_line(
|
||||||
]
|
line, "liquidity"
|
||||||
|
)
|
||||||
|
data += lines
|
||||||
reconcile_auxiliary_id = 1
|
reconcile_auxiliary_id = 1
|
||||||
if res.get("status", "") == "write_off":
|
if res.get("status", "") == "write_off":
|
||||||
data = record._recompute_suspense_line(
|
data = record._recompute_suspense_line(
|
||||||
|
@ -710,21 +672,19 @@ class AccountBankStatementLine(models.Model):
|
||||||
data, res["model"], reconcile_auxiliary_id
|
data, res["model"], reconcile_auxiliary_id
|
||||||
),
|
),
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True
|
|
||||||
)
|
)
|
||||||
elif res.get("amls"):
|
elif res.get("amls"):
|
||||||
amount = self.amount
|
amount = self.amount
|
||||||
for line in res.get("amls", []):
|
for line in res.get("amls", []):
|
||||||
line_data = record._get_reconcile_line(
|
reconcile_auxiliary_id, line_datas = record._get_reconcile_line(
|
||||||
line, "other", is_counterpart=True, max_amount=amount
|
line, "other", is_counterpart=True, max_amount=amount
|
||||||
)
|
)
|
||||||
amount -= line_data.get("amount")
|
amount -= sum(line_data.get("amount") for line_data in line_datas)
|
||||||
data.append(line_data)
|
data += line_datas
|
||||||
data = record._recompute_suspense_line(
|
data = record._recompute_suspense_line(
|
||||||
data,
|
data,
|
||||||
reconcile_auxiliary_id,
|
reconcile_auxiliary_id,
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True,
|
|
||||||
)
|
)
|
||||||
if not data.get("can_reconcile"):
|
if not data.get("can_reconcile"):
|
||||||
continue
|
continue
|
||||||
|
@ -745,14 +705,14 @@ class AccountBankStatementLine(models.Model):
|
||||||
if line["reference"] == manual_reference and line.get("id"):
|
if line["reference"] == manual_reference and line.get("id"):
|
||||||
total_amount = -line["amount"] + line["original_amount_unsigned"]
|
total_amount = -line["amount"] + line["original_amount_unsigned"]
|
||||||
original_amount = line["original_amount_unsigned"]
|
original_amount = line["original_amount_unsigned"]
|
||||||
new_data.append(
|
reconcile_auxiliary_id, lines = self._get_reconcile_line(
|
||||||
self._get_reconcile_line(
|
|
||||||
self.env["account.move.line"].browse(line["id"]),
|
self.env["account.move.line"].browse(line["id"]),
|
||||||
"other",
|
"other",
|
||||||
is_counterpart=True,
|
is_counterpart=True,
|
||||||
|
reconcile_auxiliary_id=reconcile_auxiliary_id,
|
||||||
max_amount=original_amount,
|
max_amount=original_amount,
|
||||||
)
|
)
|
||||||
)
|
new_data += lines
|
||||||
new_data.append(
|
new_data.append(
|
||||||
{
|
{
|
||||||
"reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id,
|
"reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id,
|
||||||
|
@ -777,7 +737,6 @@ class AccountBankStatementLine(models.Model):
|
||||||
new_data,
|
new_data,
|
||||||
reconcile_auxiliary_id,
|
reconcile_auxiliary_id,
|
||||||
self.manual_reference,
|
self.manual_reference,
|
||||||
exchange_recompute=True,
|
|
||||||
)
|
)
|
||||||
self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False)
|
self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False)
|
||||||
|
|
||||||
|
@ -792,18 +751,76 @@ class AccountBankStatementLine(models.Model):
|
||||||
self.move_id.to_check = False
|
self.move_id.to_check = False
|
||||||
|
|
||||||
def _get_reconcile_line(
|
def _get_reconcile_line(
|
||||||
self, line, kind, is_counterpart=False, max_amount=False, from_unreconcile=False
|
self,
|
||||||
|
line,
|
||||||
|
kind,
|
||||||
|
is_counterpart=False,
|
||||||
|
max_amount=False,
|
||||||
|
from_unreconcile=False,
|
||||||
|
reconcile_auxiliary_id=False,
|
||||||
):
|
):
|
||||||
vals = super()._get_reconcile_line(
|
new_vals = super()._get_reconcile_line(
|
||||||
line,
|
line,
|
||||||
kind,
|
kind,
|
||||||
is_counterpart=is_counterpart,
|
is_counterpart=is_counterpart,
|
||||||
max_amount=max_amount,
|
max_amount=max_amount,
|
||||||
from_unreconcile=from_unreconcile,
|
from_unreconcile=from_unreconcile,
|
||||||
)
|
)
|
||||||
|
rates = []
|
||||||
|
for vals in new_vals:
|
||||||
if vals["partner_id"] is False:
|
if vals["partner_id"] is False:
|
||||||
vals["partner_id"] = (False, self.partner_name)
|
vals["partner_id"] = (False, self.partner_name)
|
||||||
return vals
|
reconcile_auxiliary_id, rate = self._compute_exchange_rate(
|
||||||
|
vals, line, reconcile_auxiliary_id
|
||||||
|
)
|
||||||
|
if rate:
|
||||||
|
rates.append(rate)
|
||||||
|
new_vals += rates
|
||||||
|
return reconcile_auxiliary_id, new_vals
|
||||||
|
|
||||||
|
def _compute_exchange_rate(
|
||||||
|
self,
|
||||||
|
vals,
|
||||||
|
line,
|
||||||
|
reconcile_auxiliary_id,
|
||||||
|
):
|
||||||
|
foreign_currency = (
|
||||||
|
self.currency_id != self.company_id.currency_id
|
||||||
|
or self.foreign_currency_id
|
||||||
|
or vals["currency_id"] != vals["line_currency_id"]
|
||||||
|
)
|
||||||
|
if not foreign_currency or self.is_reconciled:
|
||||||
|
return reconcile_auxiliary_id, False
|
||||||
|
currency = self.env["res.currency"].browse(vals["line_currency_id"])
|
||||||
|
amount = currency._convert(
|
||||||
|
vals["currency_amount"],
|
||||||
|
self.company_id.currency_id,
|
||||||
|
self.company_id,
|
||||||
|
self.date,
|
||||||
|
) - vals.get("amount", 0)
|
||||||
|
if currency.is_zero(amount):
|
||||||
|
return reconcile_auxiliary_id, False
|
||||||
|
account = self.company_id.expense_currency_exchange_account_id
|
||||||
|
if amount < 0:
|
||||||
|
account = self.company_id.income_currency_exchange_account_id
|
||||||
|
data = {
|
||||||
|
"reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id,
|
||||||
|
"id": False,
|
||||||
|
"account_id": account.name_get()[0],
|
||||||
|
"partner_id": False,
|
||||||
|
"date": fields.Date.to_string(self.date),
|
||||||
|
"name": self.payment_ref or self.name,
|
||||||
|
"amount": amount,
|
||||||
|
"net_amount": amount,
|
||||||
|
"credit": -amount if amount < 0 else 0.0,
|
||||||
|
"debit": amount if amount > 0 else 0.0,
|
||||||
|
"kind": "other",
|
||||||
|
"currency_id": self.company_id.currency_id.id,
|
||||||
|
"line_currency_id": self.company_id.currency_id.id,
|
||||||
|
"currency_amount": amount,
|
||||||
|
}
|
||||||
|
reconcile_auxiliary_id += 1
|
||||||
|
return reconcile_auxiliary_id, data
|
||||||
|
|
||||||
def add_statement(self):
|
def add_statement(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|
|
@ -38,29 +38,29 @@ class AccountReconcileAbstract(models.AbstractModel):
|
||||||
):
|
):
|
||||||
date = self.date if "date" in self._fields else line.date
|
date = self.date if "date" in self._fields else line.date
|
||||||
original_amount = amount = net_amount = line.debit - line.credit
|
original_amount = amount = net_amount = line.debit - line.credit
|
||||||
amount_currency = self.company_id.currency_id
|
|
||||||
if is_counterpart:
|
if is_counterpart:
|
||||||
amount = line.amount_residual_currency or line.amount_residual
|
currency_amount = -line.amount_residual_currency or line.amount_residual
|
||||||
amount_currency = line.currency_id or self.company_id.currency_id
|
amount = -line.amount_residual
|
||||||
original_amount = net_amount = line.amount_residual
|
currency = line.currency_id or self.company_id.currency_id
|
||||||
|
original_amount = net_amount = -line.amount_residual
|
||||||
if max_amount:
|
if max_amount:
|
||||||
currency_max_amount = self.company_id.currency_id._convert(
|
currency_max_amount = self.company_id.currency_id._convert(
|
||||||
max_amount, amount_currency, self.company_id, line.date
|
max_amount, currency, self.company_id, date
|
||||||
)
|
)
|
||||||
if amount > currency_max_amount > 0:
|
if (
|
||||||
|
-currency_amount > currency_max_amount > 0
|
||||||
|
or -currency_amount < currency_max_amount < 0
|
||||||
|
):
|
||||||
amount = currency_max_amount
|
amount = currency_max_amount
|
||||||
net_amount = max_amount
|
net_amount = -max_amount
|
||||||
if amount < currency_max_amount < 0:
|
|
||||||
amount = currency_max_amount
|
|
||||||
net_amount = max_amount
|
|
||||||
currency_amount = -amount
|
currency_amount = -amount
|
||||||
original_amount = -original_amount
|
amount = currency._convert(
|
||||||
net_amount = -net_amount
|
currency_amount,
|
||||||
amount = amount_currency._convert(
|
self.company_id.currency_id,
|
||||||
currency_amount, self.company_id.currency_id, self.company_id, date
|
self.company_id,
|
||||||
|
date,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
amount_currency = line.currency_id
|
|
||||||
currency_amount = line.amount_currency
|
currency_amount = line.amount_currency
|
||||||
vals = {
|
vals = {
|
||||||
"reference": "account.move.line;%s" % line.id,
|
"reference": "account.move.line;%s" % line.id,
|
||||||
|
@ -96,4 +96,4 @@ class AccountReconcileAbstract(models.AbstractModel):
|
||||||
vals["original_amount_unsigned"] = original_amount
|
vals["original_amount_unsigned"] = original_amount
|
||||||
if is_counterpart:
|
if is_counterpart:
|
||||||
vals["counterpart_line_ids"] = line.ids
|
vals["counterpart_line_ids"] = line.ids
|
||||||
return vals
|
return [vals]
|
||||||
|
|
|
@ -1058,3 +1058,57 @@ class TestReconciliationWidget(TestAccountReconciliationCommon):
|
||||||
line["currency_amount"],
|
line["currency_amount"],
|
||||||
100,
|
100,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_invoice_foreign_currency_change(self):
|
||||||
|
self.env["res.currency.rate"].create(
|
||||||
|
{
|
||||||
|
"currency_id": self.env.ref("base.EUR").id,
|
||||||
|
"name": time.strftime("%Y-07-14"),
|
||||||
|
"rate": 1.15,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.env["res.currency.rate"].create(
|
||||||
|
{
|
||||||
|
"currency_id": self.env.ref("base.EUR").id,
|
||||||
|
"name": time.strftime("%Y-07-15"),
|
||||||
|
"rate": 1.2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
inv1 = self._create_invoice(
|
||||||
|
currency_id=self.currency_usd_id,
|
||||||
|
invoice_amount=100,
|
||||||
|
date_invoice="2021-07-14",
|
||||||
|
auto_validate=True,
|
||||||
|
)
|
||||||
|
bank_stmt = self.acc_bank_stmt_model.create(
|
||||||
|
{
|
||||||
|
"company_id": self.env.ref("base.main_company").id,
|
||||||
|
"journal_id": self.bank_journal_usd.id,
|
||||||
|
"date": time.strftime("%Y-07-15"),
|
||||||
|
"name": "test",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
bank_stmt_line = self.acc_bank_stmt_line_model.create(
|
||||||
|
{
|
||||||
|
"name": "testLine",
|
||||||
|
"journal_id": self.bank_journal_usd.id,
|
||||||
|
"statement_id": bank_stmt.id,
|
||||||
|
"amount": 100,
|
||||||
|
"date": time.strftime("%Y-07-15"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with Form(
|
||||||
|
bank_stmt_line,
|
||||||
|
view="account_reconcile_oca.bank_statement_line_form_reconcile_view",
|
||||||
|
) as f:
|
||||||
|
line = f.reconcile_data_info["data"][0]
|
||||||
|
self.assertEqual(
|
||||||
|
line["currency_amount"],
|
||||||
|
100,
|
||||||
|
)
|
||||||
|
f.add_account_move_line_id = inv1.line_ids.filtered(
|
||||||
|
lambda l: l.account_id.account_type == "asset_receivable"
|
||||||
|
)
|
||||||
|
self.assertFalse(f.add_account_move_line_id)
|
||||||
|
self.assertTrue(f.can_reconcile)
|
||||||
|
self.assertEqual(3, len(f.reconcile_data_info["data"]))
|
||||||
|
|
Loading…
Reference in New Issue