commit
8ed9be8316
|
@ -21,7 +21,6 @@ class AccountAccountReconcile(models.Model):
|
|||
account_id = fields.Many2one("account.account", readonly=True)
|
||||
name = fields.Char(readonly=True)
|
||||
is_reconciled = fields.Boolean(readonly=True)
|
||||
currency_id = fields.Many2one("res.currency", readonly=True)
|
||||
|
||||
@property
|
||||
def _table_query(self):
|
||||
|
@ -50,7 +49,8 @@ class AccountAccountReconcile(models.Model):
|
|||
a.id as account_id,
|
||||
FALSE as is_reconciled,
|
||||
aml.currency_id as currency_id,
|
||||
a.company_id
|
||||
a.company_id,
|
||||
false as foreign_currency_id
|
||||
"""
|
||||
|
||||
def _from(self):
|
||||
|
|
|
@ -144,6 +144,9 @@ class AccountBankStatementLine(models.Model):
|
|||
self.add_account_move_line_id = False
|
||||
|
||||
def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_reference):
|
||||
reconcile_auxiliary_id = self._compute_exchange_rate(
|
||||
data, reconcile_auxiliary_id
|
||||
)
|
||||
can_reconcile = True
|
||||
total_amount = 0
|
||||
new_data = []
|
||||
|
@ -188,7 +191,9 @@ class AccountBankStatementLine(models.Model):
|
|||
"credit": total_amount if total_amount > 0 else 0.0,
|
||||
"debit": -total_amount if total_amount < 0 else 0.0,
|
||||
"kind": "suspense",
|
||||
"currency_id": self.currency_id.id,
|
||||
"currency_id": self.company_id.currency_id.id,
|
||||
"line_currency_id": self.company_id.currency_id.id,
|
||||
"currency_amount": -total_amount,
|
||||
}
|
||||
reconcile_auxiliary_id += 1
|
||||
new_data.append(suspense_line)
|
||||
|
@ -204,7 +209,7 @@ class AccountBankStatementLine(models.Model):
|
|||
return (
|
||||
not float_is_zero(
|
||||
self.manual_amount - line["amount"],
|
||||
precision_digits=self.currency_id.decimal_places,
|
||||
precision_digits=self.company_id.currency_id.decimal_places,
|
||||
)
|
||||
or self.manual_account_id.id != line["account_id"][0]
|
||||
or self.manual_name != line["name"]
|
||||
|
@ -366,6 +371,9 @@ class AccountBankStatementLine(models.Model):
|
|||
.browse(line["account_id"])
|
||||
.name_get()[0],
|
||||
"date": fields.Date.to_string(self.date),
|
||||
"line_currency_id": self.company_id.currency_id.id,
|
||||
"currency_id": self.company_id.currency_id.id,
|
||||
"currency_amount": amount,
|
||||
}
|
||||
)
|
||||
reconcile_auxiliary_id += 1
|
||||
|
@ -376,15 +384,16 @@ class AccountBankStatementLine(models.Model):
|
|||
new_data.append(new_line)
|
||||
return new_data, reconcile_auxiliary_id
|
||||
|
||||
def _compute_exchange_rate(self, data):
|
||||
reconcile_auxiliary_id = 1
|
||||
if not self.foreign_currency_id or self.is_reconciled:
|
||||
def _compute_exchange_rate(self, data, 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
|
||||
currency_amount = self.foreign_currency_id._convert(
|
||||
self.amount_currency, currency, self.company_id, self.date
|
||||
)
|
||||
amount = sum(d["amount"] for d in data) - currency_amount
|
||||
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:
|
||||
|
@ -402,6 +411,8 @@ class AccountBankStatementLine(models.Model):
|
|||
"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
|
||||
|
@ -410,7 +421,7 @@ class AccountBankStatementLine(models.Model):
|
|||
def _default_reconcile_data(self):
|
||||
liquidity_lines, suspense_lines, other_lines = self._seek_for_lines()
|
||||
data = [self._get_reconcile_line(line, "liquidity") for line in liquidity_lines]
|
||||
reconcile_auxiliary_id = self._compute_exchange_rate(data)
|
||||
reconcile_auxiliary_id = 1
|
||||
res = (
|
||||
self.env["account.reconcile.model"]
|
||||
.search([("rule_type", "in", ["invoice_matching", "writeoff_suggestion"])])
|
||||
|
@ -424,7 +435,7 @@ class AccountBankStatementLine(models.Model):
|
|||
self.manual_reference
|
||||
)
|
||||
elif res and res.get("amls"):
|
||||
amount = self.amount
|
||||
amount = self.amount_total_signed
|
||||
for line in res.get("amls", []):
|
||||
line_data = self._get_reconcile_line(
|
||||
line, "other", is_counterpart=True, max_amount=amount
|
||||
|
@ -561,9 +572,9 @@ class AccountBankStatementLine(models.Model):
|
|||
|
||||
def unreconcile_bank_line(self):
|
||||
self.ensure_one()
|
||||
return getattr(self, "_unreconcile_bank_line_%s" % self.reconcile_mode)(
|
||||
self.reconcile_data_info["data"]
|
||||
)
|
||||
return getattr(
|
||||
self, "_unreconcile_bank_line_%s" % (self.reconcile_mode or "edit")
|
||||
)(self.reconcile_data_info["data"])
|
||||
|
||||
def _unreconcile_bank_line_edit(self, data):
|
||||
self.action_undo_reconciliation()
|
||||
|
@ -605,7 +616,7 @@ class AccountBankStatementLine(models.Model):
|
|||
record._get_reconcile_line(line, "liquidity")
|
||||
for line in liquidity_lines
|
||||
]
|
||||
reconcile_auxiliary_id = record._compute_exchange_rate(data)
|
||||
reconcile_auxiliary_id = 1
|
||||
if res.get("status", "") == "write_off":
|
||||
data = record._recompute_suspense_line(
|
||||
*record._reconcile_data_by_model(
|
||||
|
@ -664,6 +675,8 @@ class AccountBankStatementLine(models.Model):
|
|||
"debit": -total_amount if total_amount < 0 else 0.0,
|
||||
"kind": "other",
|
||||
"currency_id": line["currency_id"],
|
||||
"line_currency_id": line["currency_id"],
|
||||
"currency_amount": -total_amount,
|
||||
}
|
||||
)
|
||||
reconcile_auxiliary_id += 1
|
||||
|
|
|
@ -27,21 +27,42 @@ class AccountReconcileAbstract(models.AbstractModel):
|
|||
default=False,
|
||||
prefetch=False,
|
||||
)
|
||||
currency_id = fields.Many2one("res.currency", readonly=True)
|
||||
foreign_currency_id = fields.Many2one("res.currency")
|
||||
company_currency_id = fields.Many2one(
|
||||
"res.currency", related="company_id.currency_id"
|
||||
)
|
||||
|
||||
def _get_reconcile_line(self, line, kind, is_counterpart=False, max_amount=False):
|
||||
original_amount = amount = line.debit - line.credit
|
||||
if is_counterpart:
|
||||
original_amount = amount = (
|
||||
line.amount_residual_currency or line.amount_residual
|
||||
)
|
||||
if max_amount:
|
||||
if amount > max_amount > 0:
|
||||
amount = max_amount
|
||||
if amount < max_amount < 0:
|
||||
amount = max_amount
|
||||
date = self.date if "date" in self._fields else line.date
|
||||
original_amount = amount = net_amount = line.debit - line.credit
|
||||
amount_currency = self.company_id.currency_id
|
||||
if is_counterpart:
|
||||
amount = line.amount_residual_currency or line.amount_residual
|
||||
amount_currency = line.currency_id or self.company_id.currency_id
|
||||
original_amount = net_amount = line.amount_residual
|
||||
if max_amount:
|
||||
currency_max_amount = self.company_id.currency_id._convert(
|
||||
max_amount, amount_currency, self.company_id, line.date
|
||||
)
|
||||
if amount > currency_max_amount > 0:
|
||||
amount = currency_max_amount
|
||||
net_amount = max_amount
|
||||
if amount < currency_max_amount < 0:
|
||||
amount = currency_max_amount
|
||||
net_amount = max_amount
|
||||
amount = -amount
|
||||
original_amount = -original_amount
|
||||
net_amount = -net_amount
|
||||
else:
|
||||
amount_currency = line.currency_id
|
||||
amount = self.company_id.currency_id._convert(
|
||||
amount, amount_currency, self.company_id, date
|
||||
)
|
||||
currency_amount = amount
|
||||
amount = amount_currency._convert(
|
||||
amount, self.company_id.currency_id, self.company_id, date
|
||||
)
|
||||
vals = {
|
||||
"reference": "account.move.line;%s" % line.id,
|
||||
"id": line.id,
|
||||
|
@ -52,7 +73,10 @@ class AccountReconcileAbstract(models.AbstractModel):
|
|||
"debit": amount if amount > 0 else 0.0,
|
||||
"credit": -amount if amount < 0 else 0.0,
|
||||
"amount": amount,
|
||||
"currency_id": line.currency_id.id,
|
||||
"net_amount": amount - net_amount,
|
||||
"currency_id": self.company_id.currency_id.id,
|
||||
"line_currency_id": line.currency_id.id,
|
||||
"currency_amount": currency_amount,
|
||||
"analytic_distribution": line.analytic_distribution,
|
||||
"kind": kind,
|
||||
}
|
||||
|
|
|
@ -7,6 +7,18 @@ import session from "web.session";
|
|||
const {Component} = owl;
|
||||
|
||||
export class AccountReconcileDataWidget extends Component {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.foreignCurrency =
|
||||
this.props &&
|
||||
this.props.record &&
|
||||
(this.props.record.data.foreign_currency_id ||
|
||||
this.props.record.data.currency_id[0] !==
|
||||
this.props.record.data.company_currency_id[0] ||
|
||||
this.props.record.data[this.props.name].data.some(
|
||||
(item) => item.line_currency_id !== item.currency_id
|
||||
));
|
||||
}
|
||||
getReconcileLines() {
|
||||
var data = this.props.record.data[this.props.name].data;
|
||||
for (var line in data) {
|
||||
|
@ -31,6 +43,13 @@ export class AccountReconcileDataWidget extends Component {
|
|||
currency: session.get_currency(data[line].currency_id),
|
||||
}
|
||||
);
|
||||
data[line].amount_currency_format = fieldUtils.format.monetary(
|
||||
data[line].currency_amount,
|
||||
undefined,
|
||||
{
|
||||
currency: session.get_currency(data[line].line_currency_id),
|
||||
}
|
||||
);
|
||||
if (data[line].original_amount) {
|
||||
data[line].original_amount_format = fieldUtils.format.monetary(
|
||||
data[line].original_amount,
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
<th>Partner</th>
|
||||
<th>Date</th>
|
||||
<th>Label</th>
|
||||
<th class="text-end" t-if="foreignCurrency">
|
||||
Amount in currency
|
||||
</th>
|
||||
<th class="text-end">Debit</th>
|
||||
<th class="text-end">Credit</th>
|
||||
<th />
|
||||
|
@ -90,6 +93,12 @@
|
|||
t-if="reconcile_line.name"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
class="text-end o_field_account_reconcile_oca_balance_float"
|
||||
t-if="foreignCurrency"
|
||||
>
|
||||
<span t-esc="reconcile_line.amount_currency_format" />
|
||||
</td>
|
||||
<td
|
||||
class="text-end o_field_account_reconcile_oca_balance_float"
|
||||
>
|
||||
|
|
|
@ -252,11 +252,71 @@ class TestReconciliationWidget(TestAccountReconciliationCommon):
|
|||
f.manual_reference = "account.move.line;%s" % receivable2.id
|
||||
self.assertEqual(f.manual_amount, -30)
|
||||
self.assertTrue(f.can_reconcile)
|
||||
self.assertEqual(inv1.amount_residual, 100)
|
||||
self.assertEqual(inv2.amount_residual, 100)
|
||||
self.assertEqual(inv1.amount_residual_signed, 100)
|
||||
self.assertEqual(inv2.amount_residual_signed, 100)
|
||||
bank_stmt_line.reconcile_bank_line()
|
||||
self.assertEqual(inv1.amount_residual, 30)
|
||||
self.assertEqual(inv2.amount_residual, 70)
|
||||
self.assertEqual(inv1.amount_residual_signed, 30)
|
||||
self.assertEqual(inv2.amount_residual_signed, 70)
|
||||
|
||||
def test_reconcile_invoice_partial_supplier(self):
|
||||
"""
|
||||
We want to partially reconcile two invoices from a single payment.
|
||||
As a result, both invoices must be partially reconciled
|
||||
"""
|
||||
inv1 = self.create_invoice(
|
||||
currency_id=self.currency_euro_id,
|
||||
invoice_amount=100,
|
||||
move_type="in_invoice",
|
||||
)
|
||||
inv2 = self.create_invoice(
|
||||
currency_id=self.currency_euro_id,
|
||||
invoice_amount=100,
|
||||
move_type="in_invoice",
|
||||
)
|
||||
bank_stmt = self.acc_bank_stmt_model.create(
|
||||
{
|
||||
"company_id": self.env.ref("base.main_company").id,
|
||||
"journal_id": self.bank_journal_euro.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_euro.id,
|
||||
"statement_id": bank_stmt.id,
|
||||
"amount": -100,
|
||||
"date": time.strftime("%Y-07-15"),
|
||||
}
|
||||
)
|
||||
receivable1 = inv1.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type == "liability_payable"
|
||||
)
|
||||
receivable2 = inv2.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type == "liability_payable"
|
||||
)
|
||||
with Form(
|
||||
bank_stmt_line,
|
||||
view="account_reconcile_oca.bank_statement_line_form_reconcile_view",
|
||||
) as f:
|
||||
self.assertFalse(f.can_reconcile)
|
||||
f.add_account_move_line_id = receivable1
|
||||
self.assertFalse(f.add_account_move_line_id)
|
||||
self.assertTrue(f.can_reconcile)
|
||||
f.manual_reference = "account.move.line;%s" % receivable1.id
|
||||
self.assertEqual(f.manual_amount, 100)
|
||||
f.manual_amount = 70
|
||||
self.assertFalse(f.can_reconcile)
|
||||
f.add_account_move_line_id = receivable2
|
||||
f.manual_reference = "account.move.line;%s" % receivable2.id
|
||||
self.assertEqual(f.manual_amount, 30)
|
||||
self.assertTrue(f.can_reconcile)
|
||||
self.assertEqual(inv1.amount_residual_signed, -100)
|
||||
self.assertEqual(inv2.amount_residual_signed, -100)
|
||||
bank_stmt_line.reconcile_bank_line()
|
||||
self.assertEqual(inv1.amount_residual_signed, -30)
|
||||
self.assertEqual(inv2.amount_residual_signed, -70)
|
||||
|
||||
def test_reconcile_model(self):
|
||||
"""
|
||||
|
@ -860,3 +920,36 @@ class TestReconciliationWidget(TestAccountReconciliationCommon):
|
|||
.partner_id,
|
||||
parent_partner,
|
||||
)
|
||||
|
||||
def test_journal_foreign_currency(self):
|
||||
inv1 = self.create_invoice(currency_id=self.currency_usd_id, invoice_amount=100)
|
||||
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:
|
||||
self.assertFalse(f.can_reconcile)
|
||||
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.assertTrue(bank_stmt_line.can_reconcile)
|
||||
bank_stmt_line.reconcile_bank_line()
|
||||
self.assertEqual(0, inv1.amount_residual)
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<form js_class="reconcile_manual" create="0">
|
||||
<field name="manual_delete" invisible="1" />
|
||||
<field name="manual_reference" invisible="1" />
|
||||
<field name="company_currency_id" invisible="1" />
|
||||
<field name="foreign_currency_id" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<div class="o_form_statusbar o_account_reconcile_oca_statusbar">
|
||||
<div class="o_statusbar_buttons">
|
||||
<button
|
||||
|
|
|
@ -192,6 +192,8 @@
|
|||
<field name="manual_delete" invisible="1" />
|
||||
<field name="is_reconciled" invisible="1" />
|
||||
<field name="foreign_currency_id" invisible="1" />
|
||||
<field name="company_currency_id" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field
|
||||
name="reconcile_data_info"
|
||||
nolabel="1"
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
<field name="date" />
|
||||
<field name="move_id" />
|
||||
<field name="partner_id" />
|
||||
<field name="company_currency_id" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field name="name" />
|
||||
<field name="amount_residual_currency" optional="hide" />
|
||||
<field name="amount_residual" />
|
||||
<button
|
||||
type="object"
|
||||
|
|
Loading…
Reference in New Issue