199 lines
6.7 KiB
Python
199 lines
6.7 KiB
Python
# Copyright 2023 Dixmit
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class CharId(fields.Id):
|
|
type = "string"
|
|
column_type = ("varchar", fields.pg_varchar())
|
|
|
|
|
|
class AccountAccountReconcile(models.Model):
|
|
_name = "account.account.reconcile"
|
|
_description = "Account Account Reconcile"
|
|
_inherit = "account.reconcile.abstract"
|
|
_auto = False
|
|
|
|
reconcile_data_info = fields.Serialized(inverse="_inverse_reconcile_data_info")
|
|
|
|
partner_id = fields.Many2one("res.partner", readonly=True)
|
|
account_id = fields.Many2one("account.account", readonly=True)
|
|
name = fields.Char(readonly=True)
|
|
is_reconciled = fields.Boolean(readonly=True)
|
|
|
|
@property
|
|
def _table_query(self):
|
|
return "%s %s %s %s %s" % (
|
|
self._select(),
|
|
self._from(),
|
|
self._where(),
|
|
self._groupby(),
|
|
self._having(),
|
|
)
|
|
|
|
def _select(self):
|
|
account_account_name_field = (
|
|
self.env["ir.model.fields"]
|
|
.sudo()
|
|
.search([("model", "=", "account.account"), ("name", "=", "name")])
|
|
)
|
|
account_name = (
|
|
f"a.name ->> '{self.env.user.lang}'"
|
|
if account_account_name_field.translate
|
|
else "a.name"
|
|
)
|
|
return f"""
|
|
SELECT
|
|
min(aml.id) as id,
|
|
MAX({account_name}) as name,
|
|
CASE
|
|
WHEN a.account_type in ('asset_receivable', 'liability_payable')
|
|
THEN aml.partner_id
|
|
ELSE NULL
|
|
END as partner_id,
|
|
a.id as account_id,
|
|
FALSE as is_reconciled,
|
|
aml.currency_id as currency_id,
|
|
a.company_id,
|
|
false as foreign_currency_id
|
|
"""
|
|
|
|
def _from(self):
|
|
return """
|
|
FROM
|
|
account_account a
|
|
INNER JOIN account_move_line aml ON aml.account_id = a.id
|
|
INNER JOIN account_move am ON am.id = aml.move_id
|
|
"""
|
|
|
|
def _where(self):
|
|
return """
|
|
WHERE a.reconcile
|
|
AND am.state = 'posted'
|
|
AND aml.amount_residual != 0
|
|
"""
|
|
|
|
def _groupby(self):
|
|
return """
|
|
GROUP BY
|
|
a.id,
|
|
CASE
|
|
WHEN a.account_type in ('asset_receivable', 'liability_payable')
|
|
THEN aml.partner_id
|
|
ELSE NULL
|
|
END,
|
|
aml.currency_id,
|
|
a.company_id
|
|
"""
|
|
|
|
def _having(self):
|
|
return """
|
|
HAVING
|
|
SUM(aml.debit) > 0
|
|
AND SUM(aml.credit) > 0
|
|
"""
|
|
|
|
def _compute_reconcile_data_info(self):
|
|
data_obj = self.env["account.account.reconcile.data"]
|
|
for record in self:
|
|
if self.env.context.get("default_account_move_lines"):
|
|
data = {
|
|
"data": [],
|
|
"counterparts": self.env.context.get("default_account_move_lines"),
|
|
}
|
|
record.reconcile_data_info = self._recompute_data(data)
|
|
continue
|
|
data_record = data_obj.search(
|
|
[("user_id", "=", self.env.user.id), ("reconcile_id", "=", record.id)]
|
|
)
|
|
if data_record:
|
|
record.reconcile_data_info = data_record.data
|
|
else:
|
|
record.reconcile_data_info = {"data": [], "counterparts": []}
|
|
|
|
def _inverse_reconcile_data_info(self):
|
|
data_obj = self.env["account.account.reconcile.data"]
|
|
for record in self:
|
|
data_record = data_obj.search(
|
|
[("user_id", "=", self.env.user.id), ("reconcile_id", "=", record.id)]
|
|
)
|
|
if data_record:
|
|
data_record.data = record.reconcile_data_info
|
|
else:
|
|
data_obj.create(
|
|
{
|
|
"reconcile_id": record.id,
|
|
"user_id": self.env.user.id,
|
|
"data": record.reconcile_data_info,
|
|
}
|
|
)
|
|
|
|
@api.onchange("add_account_move_line_id")
|
|
def _onchange_add_account_move_line(self):
|
|
if self.add_account_move_line_id:
|
|
data = self.reconcile_data_info
|
|
if self.add_account_move_line_id.id not in data["counterparts"]:
|
|
data["counterparts"].append(self.add_account_move_line_id.id)
|
|
else:
|
|
del data["counterparts"][
|
|
data["counterparts"].index(self.add_account_move_line_id.id)
|
|
]
|
|
self.reconcile_data_info = self._recompute_data(data)
|
|
self.add_account_move_line_id = False
|
|
|
|
@api.onchange("manual_reference", "manual_delete")
|
|
def _onchange_manual_reconcile_reference(self):
|
|
self.ensure_one()
|
|
data = self.reconcile_data_info
|
|
counterparts = []
|
|
for line in data["data"]:
|
|
if line["reference"] == self.manual_reference:
|
|
if self.manual_delete:
|
|
continue
|
|
counterparts.append(line["id"])
|
|
data["counterparts"] = counterparts
|
|
self.reconcile_data_info = self._recompute_data(data)
|
|
self.manual_delete = False
|
|
self.manual_reference = False
|
|
|
|
def _recompute_data(self, data):
|
|
new_data = {"data": [], "counterparts": data["counterparts"]}
|
|
counterparts = data["counterparts"]
|
|
amount = 0.0
|
|
for line_id in counterparts:
|
|
max_amount = amount if line_id == counterparts[-1] else 0
|
|
lines = self._get_reconcile_line(
|
|
self.env["account.move.line"].browse(line_id),
|
|
"other",
|
|
is_counterpart=True,
|
|
max_amount=max_amount,
|
|
move=True,
|
|
)
|
|
new_data["data"] += lines
|
|
amount += sum(line["amount"] for line in lines)
|
|
return new_data
|
|
|
|
def clean_reconcile(self):
|
|
self.ensure_one()
|
|
self.reconcile_data_info = {"data": [], "counterparts": []}
|
|
|
|
def reconcile(self):
|
|
lines = self.env["account.move.line"].browse(
|
|
self.reconcile_data_info["counterparts"]
|
|
)
|
|
lines.reconcile()
|
|
data_record = self.env["account.account.reconcile.data"].search(
|
|
[("user_id", "=", self.env.user.id), ("reconcile_id", "=", self.id)]
|
|
)
|
|
data_record.unlink()
|
|
|
|
|
|
class AccountAccountReconcileData(models.TransientModel):
|
|
_name = "account.account.reconcile.data"
|
|
_description = "Reconcile data model to store user info"
|
|
|
|
user_id = fields.Many2one("res.users", required=True)
|
|
reconcile_id = fields.Integer(required=True)
|
|
data = fields.Serialized()
|