[IMP] account_reconcile_oca: Improve multicurrency management.

We will use currency of the line in order to get the suspense and max line value
pull/694/head
Enric Tobella 2024-09-13 10:33:50 +02:00
parent 3e6180fd21
commit 62d508bb20
5 changed files with 195 additions and 27 deletions

View File

@ -164,7 +164,11 @@ class AccountAccountReconcile(models.Model):
for line_id in counterparts: for line_id in counterparts:
max_amount = amount if line_id == counterparts[-1] else 0 max_amount = amount if line_id == counterparts[-1] else 0
lines = self._get_reconcile_line( lines = self._get_reconcile_line(
self.env["account.move.line"].browse(line_id), "other", True, max_amount self.env["account.move.line"].browse(line_id),
"other",
True,
max_amount,
move=True,
) )
new_data["data"] += lines new_data["data"] += lines
amount += sum(line["amount"] for line in lines) amount += sum(line["amount"] for line in lines)

View File

@ -201,9 +201,17 @@ class AccountBankStatementLine(models.Model):
new_data = [] new_data = []
is_new_line = True is_new_line = True
pending_amount = 0.0 pending_amount = 0.0
currency = self._get_reconcile_currency()
for line in data: for line in data:
if line["kind"] != "suspense": if line["kind"] != "suspense":
pending_amount += line["amount"] pending_amount += currency._convert(
line["currency_amount"],
self.env["res.currency"].browse(
line.get("line_currency_id", currency.id)
),
self.company_id,
self.date,
)
if self.add_account_move_line_id.id in line.get( if self.add_account_move_line_id.id in line.get(
"counterpart_line_ids", [] "counterpart_line_ids", []
): ):
@ -212,7 +220,11 @@ class AccountBankStatementLine(models.Model):
new_data.append(line) new_data.append(line)
if is_new_line: if is_new_line:
reconcile_auxiliary_id, lines = self._get_reconcile_line( reconcile_auxiliary_id, lines = self._get_reconcile_line(
self.add_account_move_line_id, "other", True, pending_amount self.add_account_move_line_id,
"other",
True,
max_amount=pending_amount,
move=True,
) )
new_data += lines new_data += lines
self.reconcile_data_info = self._recompute_suspense_line( self.reconcile_data_info = self._recompute_suspense_line(
@ -226,6 +238,7 @@ class AccountBankStatementLine(models.Model):
def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_reference): def _recompute_suspense_line(self, data, reconcile_auxiliary_id, manual_reference):
can_reconcile = True can_reconcile = True
total_amount = 0 total_amount = 0
currency_amount = 0
new_data = [] new_data = []
suspense_line = False suspense_line = False
counterparts = [] counterparts = []
@ -240,10 +253,28 @@ class AccountBankStatementLine(models.Model):
if line["kind"] != "suspense": if line["kind"] != "suspense":
new_data.append(line) new_data.append(line)
total_amount += line["amount"] total_amount += line["amount"]
if line.get("currency_amount"):
currency_amount += (
self.env["res.currency"]
.browse(line["line_currency_id"])
._convert(
line["currency_amount"],
self._get_reconcile_currency(),
self.company_id,
self.date,
)
)
else:
currency_amount += self.company_id.currency_id._convert(
line["amount"],
self._get_reconcile_currency(),
self.company_id,
self.date,
)
else: else:
suspense_line = line suspense_line = line
if not float_is_zero( if not float_is_zero(
total_amount, precision_digits=self.currency_id.decimal_places total_amount, precision_digits=self.company_id.currency_id.decimal_places
): ):
can_reconcile = False can_reconcile = False
if suspense_line: if suspense_line:
@ -255,6 +286,7 @@ class AccountBankStatementLine(models.Model):
} }
) )
else: else:
suspense_line = { suspense_line = {
"reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id, "reference": "reconcile_auxiliary;%s" % reconcile_auxiliary_id,
"id": False, "id": False,
@ -269,8 +301,8 @@ class AccountBankStatementLine(models.Model):
"debit": -total_amount if total_amount < 0 else 0.0, "debit": -total_amount if total_amount < 0 else 0.0,
"kind": "suspense", "kind": "suspense",
"currency_id": self.company_id.currency_id.id, "currency_id": self.company_id.currency_id.id,
"line_currency_id": self.company_id.currency_id.id, "line_currency_id": self.currency_id.id,
"currency_amount": -total_amount, "currency_amount": -currency_amount,
} }
reconcile_auxiliary_id += 1 reconcile_auxiliary_id += 1
new_data.append(suspense_line) new_data.append(suspense_line)
@ -375,7 +407,7 @@ class AccountBankStatementLine(models.Model):
if self.manual_line_id.exists() and self.manual_line_id: if self.manual_line_id.exists() and self.manual_line_id:
self.manual_amount = self.manual_in_currency_id._convert( self.manual_amount = self.manual_in_currency_id._convert(
self.manual_amount_in_currency, self.manual_amount_in_currency,
self.company_id.currency_id, self._get_reconcile_currency(),
self.company_id, self.company_id,
self.manual_line_id.date, self.manual_line_id.date,
) )
@ -529,7 +561,10 @@ class AccountBankStatementLine(models.Model):
reconcile_auxiliary_id = 1 reconcile_auxiliary_id = 1
for line in liquidity_lines: for line in liquidity_lines:
reconcile_auxiliary_id, lines = self._get_reconcile_line( reconcile_auxiliary_id, lines = self._get_reconcile_line(
line, "liquidity", reconcile_auxiliary_id=reconcile_auxiliary_id line,
"liquidity",
reconcile_auxiliary_id=reconcile_auxiliary_id,
move=True,
) )
data += lines data += lines
if not from_unreconcile: if not from_unreconcile:
@ -574,16 +609,87 @@ class AccountBankStatementLine(models.Model):
self.manual_reference, self.manual_reference,
) )
for line in other_lines: for line in other_lines:
partial_lines = self._all_partials_lines(line) if from_unreconcile else []
if partial_lines:
for reconciled_line in (
partial_lines.debit_move_id + partial_lines.credit_move_id - line
):
if (
reconciled_line.move_id.journal_id
== self.company_id.currency_exchange_journal_id
):
reconcile_auxiliary_id, lines = self._get_reconcile_line( reconcile_auxiliary_id, lines = self._get_reconcile_line(
line, "other", from_unreconcile=from_unreconcile reconciled_line.move_id.line_ids - reconciled_line,
"other",
from_unreconcile=False,
move=True,
) )
data += lines data += lines
continue
partial = partial_lines.filtered(
lambda r: r.debit_move_id == reconciled_line
or r.credit_move_id == reconciled_line
)
partial_amount = sum(
partial.filtered(
lambda r: r.credit_move_id == reconciled_line
).mapped("amount")
) - sum(
partial.filtered(
lambda r: r.debit_move_id == reconciled_line
).mapped("amount")
)
reconcile_auxiliary_id, lines = self._get_reconcile_line(
reconciled_line,
"other",
from_unreconcile={
"amount": partial_amount,
"credit": partial_amount > 0 and partial_amount,
"debit": partial_amount < 0 and -partial_amount,
"currency_amount": sum(
partial.filtered(
lambda r: r.credit_move_id == reconciled_line
).mapped("credit_amount_currency")
)
- sum(
partial.filtered(
lambda r: r.debit_move_id == reconciled_line
).mapped("debit_amount_currency")
),
},
move=True,
)
data += lines
else:
reconcile_auxiliary_id, lines = self._get_reconcile_line(
line, "other", from_unreconcile=False
)
data += lines
return self._recompute_suspense_line( return self._recompute_suspense_line(
data, data,
reconcile_auxiliary_id, reconcile_auxiliary_id,
self.manual_reference, self.manual_reference,
) )
def _all_partials_lines(self, lines):
reconciliation_lines = lines.filtered(
lambda x: x.account_id.reconcile
or x.account_id.account_type in ("asset_cash", "liability_credit_card")
)
current_lines = reconciliation_lines
current_partials = self.env["account.partial.reconcile"]
partials = self.env["account.partial.reconcile"]
while current_lines:
current_partials = (
current_lines.matched_debit_ids + current_lines.matched_credit_ids
) - current_partials
current_lines = (
current_partials.debit_move_id + current_partials.credit_move_id
) - current_lines
partials += current_partials
return partials
def clean_reconcile(self): def clean_reconcile(self):
self.reconcile_data_info = self._default_reconcile_data() self.reconcile_data_info = self._default_reconcile_data()
self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False) self.can_reconcile = self.reconcile_data_info.get("can_reconcile", False)
@ -737,12 +843,13 @@ class AccountBankStatementLine(models.Model):
to_reverse._reverse_moves(default_values_list, cancel=True) to_reverse._reverse_moves(default_values_list, cancel=True)
def _reconcile_move_line_vals(self, line, move_id=False): def _reconcile_move_line_vals(self, line, move_id=False):
return { vals = {
"move_id": move_id or self.move_id.id, "move_id": move_id or self.move_id.id,
"account_id": line["account_id"][0], "account_id": line["account_id"][0],
"partner_id": line.get("partner_id") and line["partner_id"][0], "partner_id": line.get("partner_id") and line["partner_id"][0],
"credit": line["credit"], "credit": line["credit"],
"debit": line["debit"], "debit": line["debit"],
"currency_id": line.get("line_currency_id", self.company_id.currency_id.id),
"tax_ids": line.get("tax_ids", []), "tax_ids": line.get("tax_ids", []),
"tax_tag_ids": line.get("tax_tag_ids", []), "tax_tag_ids": line.get("tax_tag_ids", []),
"group_tax_id": line.get("group_tax_id"), "group_tax_id": line.get("group_tax_id"),
@ -751,6 +858,11 @@ class AccountBankStatementLine(models.Model):
"name": line.get("name"), "name": line.get("name"),
"reconcile_model_id": line.get("reconcile_model_id"), "reconcile_model_id": line.get("reconcile_model_id"),
} }
if line.get("line_currency_id") and line["currency_id"] != line.get(
"line_currency_id"
):
vals["amount_currency"] = line["currency_amount"]
return vals
@api.model_create_multi @api.model_create_multi
def create(self, mvals): def create(self, mvals):
@ -770,7 +882,9 @@ class AccountBankStatementLine(models.Model):
data = [] data = []
for line in liquidity_lines: for line in liquidity_lines:
reconcile_auxiliary_id, lines = record._get_reconcile_line( reconcile_auxiliary_id, lines = record._get_reconcile_line(
line, "liquidity" line,
"liquidity",
move=True,
) )
data += lines data += lines
reconcile_auxiliary_id = 1 reconcile_auxiliary_id = 1
@ -785,7 +899,7 @@ class AccountBankStatementLine(models.Model):
amount = self.amount amount = self.amount
for line in res.get("amls", []): for line in res.get("amls", []):
reconcile_auxiliary_id, line_datas = 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, move=True
) )
amount -= sum(line_data.get("amount") for line_data in line_datas) amount -= sum(line_data.get("amount") for line_data in line_datas)
data += line_datas data += line_datas
@ -847,6 +961,7 @@ class AccountBankStatementLine(models.Model):
is_counterpart=True, is_counterpart=True,
reconcile_auxiliary_id=reconcile_auxiliary_id, reconcile_auxiliary_id=reconcile_auxiliary_id,
max_amount=original_amount, max_amount=original_amount,
move=True,
) )
new_data += lines new_data += lines
new_data.append( new_data.append(
@ -894,6 +1009,7 @@ class AccountBankStatementLine(models.Model):
max_amount=False, max_amount=False,
from_unreconcile=False, from_unreconcile=False,
reconcile_auxiliary_id=False, reconcile_auxiliary_id=False,
move=False,
): ):
new_vals = super()._get_reconcile_line( new_vals = super()._get_reconcile_line(
line, line,
@ -901,6 +1017,7 @@ class AccountBankStatementLine(models.Model):
is_counterpart=is_counterpart, is_counterpart=is_counterpart,
max_amount=max_amount, max_amount=max_amount,
from_unreconcile=from_unreconcile, from_unreconcile=from_unreconcile,
move=move,
) )
rates = [] rates = []
for vals in new_vals: for vals in new_vals:
@ -989,3 +1106,10 @@ class AccountBankStatementLine(models.Model):
"split_line_id": self.id, "split_line_id": self.id,
} }
return action return action
def _get_reconcile_currency(self):
return (
self.currency_id
or self.journal_id.currency_id
or self.company_id._currency_id
)

View File

@ -33,24 +33,39 @@ class AccountReconcileAbstract(models.AbstractModel):
related="company_id.currency_id", string="Company Currency" related="company_id.currency_id", string="Company Currency"
) )
def _get_reconcile_currency(self):
return self.currency_id or self.company_id._currency_id
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,
move=False,
): ):
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
if is_counterpart: if is_counterpart:
currency_amount = -line.amount_residual_currency or line.amount_residual currency_amount = -line.amount_residual_currency or line.amount_residual
amount = -line.amount_residual amount = -line.amount_residual
currency = line.currency_id or self.company_id.currency_id currency = line.currency_id or line.company_id.currency_id
original_amount = net_amount = -line.amount_residual original_amount = net_amount = -line.amount_residual
if max_amount: if max_amount:
currency_max_amount = self.company_id.currency_id._convert( real_currency_amount = currency._convert(
max_amount, currency, self.company_id, date currency_amount,
self._get_reconcile_currency(),
self.company_id,
date,
) )
if ( if (
-currency_amount > currency_max_amount > 0 -real_currency_amount > max_amount > 0
or -currency_amount < currency_max_amount < 0 or -real_currency_amount < max_amount < 0
): ):
currency_max_amount = self._get_reconcile_currency()._convert(
max_amount, currency, self.company_id, date
)
amount = currency_max_amount amount = currency_max_amount
net_amount = -max_amount net_amount = -max_amount
currency_amount = -amount currency_amount = -amount
@ -63,6 +78,8 @@ class AccountReconcileAbstract(models.AbstractModel):
else: else:
currency_amount = line.amount_currency currency_amount = line.amount_currency
vals = { vals = {
"move_id": move and line.move_id.id,
"move": move and line.move_id.name,
"reference": "account.move.line;%s" % line.id, "reference": "account.move.line;%s" % line.id,
"id": line.id, "id": line.id,
"account_id": line.account_id.name_get()[0], "account_id": line.account_id.name_get()[0],
@ -82,11 +99,11 @@ class AccountReconcileAbstract(models.AbstractModel):
if from_unreconcile: if from_unreconcile:
vals.update( vals.update(
{ {
"id": False, "credit": vals["debit"] and from_unreconcile["debit"],
"counterpart_line_ids": ( "debit": vals["credit"] and from_unreconcile["credit"],
line.matched_debit_ids.mapped("debit_move_id") "amount": from_unreconcile["amount"],
| line.matched_credit_ids.mapped("credit_move_id") "net_amount": from_unreconcile["amount"],
).ids, "currency_amount": from_unreconcile["currency_amount"],
} }
) )
if not float_is_zero( if not float_is_zero(

View File

@ -3,12 +3,15 @@
import fieldUtils from "web.field_utils"; import fieldUtils from "web.field_utils";
import {registry} from "@web/core/registry"; import {registry} from "@web/core/registry";
import session from "web.session"; import session from "web.session";
import {useService} from "@web/core/utils/hooks";
const {Component} = owl; const {Component} = owl;
export class AccountReconcileDataWidget extends Component { export class AccountReconcileDataWidget extends Component {
setup() { setup() {
super.setup(...arguments); super.setup(...arguments);
this.orm = useService("orm");
this.action = useService("action");
this.foreignCurrency = this.foreignCurrency =
this.props && this.props &&
this.props.record && this.props.record &&
@ -83,6 +86,15 @@ export class AccountReconcileDataWidget extends Component {
}); });
this.env.bus.trigger("RECONCILE_PAGE_NAVIGATE", triggerEv); this.env.bus.trigger("RECONCILE_PAGE_NAVIGATE", triggerEv);
} }
async openMove(ev, moveId) {
ev.preventDefault();
ev.stopPropagation();
console.log(moveId);
const action = await this.orm.call("account.move", "get_formview_action", [
[moveId],
]);
this.action.doAction(action);
}
} }
AccountReconcileDataWidget.template = "account_reconcile_oca.ReconcileDataWidget"; AccountReconcileDataWidget.template = "account_reconcile_oca.ReconcileDataWidget";

View File

@ -116,7 +116,18 @@
t-on-click="(ev) => this.selectReconcileLine(ev, reconcile_line)" t-on-click="(ev) => this.selectReconcileLine(ev, reconcile_line)"
t-att-class="'o_reconcile_widget_line ' + reconcile_line.kind + (props.record.data.manual_reference == reconcile_line.reference ? ' selected ' : ' ')" t-att-class="'o_reconcile_widget_line ' + reconcile_line.kind + (props.record.data.manual_reference == reconcile_line.reference ? ' selected ' : ' ')"
> >
<td t-esc="reconcile_line.account_id[1]" /> <td>
<div t-esc="reconcile_line.account_id[1]" />
<div t-if="reconcile_line.move_id">
<a
t-att-href="'/web#id=' + reconcile_line.move_id + '&amp;view_type=form&amp;model=account.move'"
class="o_form_uri"
t-on-click="(ev) => this.openMove(ev, reconcile_line.move_id)"
>
<small t-esc="reconcile_line.move" />
</a>
</div>
</td>
<td> <td>
<span <span
t-esc="reconcile_line.partner_id[1]" t-esc="reconcile_line.partner_id[1]"