2
0
account-financial-tools/account_netting/wizards/account_move_make_netting.py
2023-11-03 17:37:41 +01:00

181 lines
6.8 KiB
Python

# Copyright 2015 Pedro M. Baeza
# Copyright 2017 Tecnativa - Vicent Cubells
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class AccountMoveMakeNetting(models.TransientModel):
_name = "account.move.make.netting"
_description = "Wizard to generate account moves for netting"
_check_company_auto = True
company_id = fields.Many2one("res.company", required=True)
journal_id = fields.Many2one(
comodel_name="account.journal",
required=True,
domain="[('type', '=', 'general'), ('company_id', '=', company_id)]",
check_company=True,
)
move_line_ids = fields.Many2many(
comodel_name="account.move.line",
check_company=True,
string="Journal Items to Compensate",
)
partner_id = fields.Many2one("res.partner", readonly=True)
company_currency_id = fields.Many2one(related="company_id.currency_id")
balance = fields.Monetary(readonly=True, currency_field="company_currency_id")
balance_type = fields.Selection(
selection=[("pay", "To pay"), ("receive", "To receive")],
readonly=True,
)
@api.model
def default_get(self, fields_list):
if len(self.env.context.get("active_ids", [])) < 2:
raise UserError(_("You should select at least 2 journal items."))
move_lines = self.env["account.move.line"].browse(
self.env.context["active_ids"]
)
partners = self.env["res.partner"]
for line in move_lines:
if line.parent_state != "posted":
raise UserError(_("Line '%s' is not posted.") % line.display_name)
if line.account_id.account_type not in (
"liability_payable",
"asset_receivable",
):
raise UserError(
_(
"Line '%(line)s' has account '%(account)s' which is not "
"a payable nor a receivable account."
)
% {
"line": line.display_name,
"account": line.account_id.display_name,
}
)
if line.reconciled:
raise UserError(
_("Line '%s' is already reconciled.") % line.display_name
)
if not line.partner_id:
raise UserError(
_("Line '%s' doesn't have a partner.") % line.display_name
)
partners |= line.partner_id
if len(move_lines.account_id) == 1:
raise UserError(
_(
"The 'Compensate' function is intended to balance "
"operations on different accounts for the same partner. "
"The selected journal items have the same "
"account '%s', so you should use the 'Reconcile' function instead."
)
% move_lines.account_id.display_name
)
if len(partners) != 1:
raise UserError(
_(
"The selected journal items have different partners: %s. "
"All the selected journal items must have the same partner."
)
% ", ".join([p.display_name for p in partners])
)
res = super().default_get(fields_list)
company = self.env.company
ccur = company.currency_id
debit_move_lines_debit = move_lines.filtered("debit")
credit_move_lines_debit = move_lines.filtered("credit")
balance = ccur.round(
abs(sum(debit_move_lines_debit.mapped("amount_residual")))
- abs(sum(credit_move_lines_debit.mapped("amount_residual")))
)
res.update(
{
"balance": abs(balance),
"balance_type": "pay"
if ccur.compare_amounts(balance, 0) < 0
else "receive",
"company_id": company.id,
"move_line_ids": move_lines.ids,
"partner_id": partners.id,
}
)
return res
def _prepare_account_move(self):
# Group amounts by account
account_groups = self.move_line_ids.read_group(
[("id", "in", self.move_line_ids.ids)],
["account_id", "amount_residual"],
["account_id"],
)
debtors = []
creditors = []
total_debtors = 0.0
total_creditors = 0.0
ccur = self.company_id.currency_id
for account_group in account_groups:
balance = account_group["amount_residual"]
group_vals = {
"account_id": account_group["account_id"][0],
"balance": abs(balance),
}
if ccur.compare_amounts(balance, 0) > 0:
debtors.append(group_vals)
total_debtors += balance
else:
creditors.append(group_vals)
total_creditors += abs(balance)
# Compute move lines
netting_amount = min(total_creditors, total_debtors)
field_map = {1: "debit", 0: "credit"}
move_lines = []
for i, group in enumerate([debtors, creditors]):
available_amount = netting_amount
for account_group in group:
move_line_vals = {
field_map[i]: min(available_amount, account_group["balance"]),
"partner_id": self.partner_id.id,
"account_id": account_group["account_id"],
}
move_lines.append((0, 0, move_line_vals))
available_amount -= account_group["balance"]
if ccur.compare_amounts(available_amount, 0) <= 0:
break
vals = {
"ref": _("AR/AP netting"),
"journal_id": self.journal_id.id,
"company_id": self.company_id.id,
"line_ids": move_lines,
}
return vals
def button_compensate(self):
self.ensure_one()
# Create account move
move = self.env["account.move"].create(self._prepare_account_move())
move.action_post()
# Make reconciliation
for move_line in move.line_ids:
to_reconcile = move_line + self.move_line_ids.filtered(
lambda x: x.account_id == move_line.account_id
)
to_reconcile.reconcile()
# Open created move
action = self.env["ir.actions.actions"]._for_xml_id(
"account.action_move_journal_line"
)
action.update(
{
"view_mode": "form",
"views": False,
"view_id": False,
"res_id": move.id,
}
)
return action